1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#!/usr/bin/python 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved. 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)# found in the LICENSE file. 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import collections 7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import hashlib 8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import operator 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import os 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import re 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import sys 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)RESOURCE_EXTRACT_REGEX = re.compile('^#define (\S*) (\d*)$', re.MULTILINE) 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class Error(Exception): 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Base error class for all exceptions in generated_resources_map.""" 18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class HashCollisionError(Error): 21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Multiple resource names hash to the same value.""" 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)Resource = collections.namedtuple("Resource", ['hash', 'name', 'index']) 25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)def _HashName(name): 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Returns the hash id for a name. 29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Args: 31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) name: The name to hash. 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Returns: 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) An int that is at most 32 bits. 35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """ 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) md5hash = hashlib.md5() 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) md5hash.update(name) 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return int(md5hash.hexdigest()[:8], 16) 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)def _GetNameIndexPairsIter(string_to_scan): 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Gets an iterator of the resource name and index pairs of the given string. 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Scans the input string for lines of the form "#define NAME INDEX" and returns 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) an iterator over all matching (NAME, INDEX) pairs. 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Args: 48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) string_to_scan: The input string to scan. 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Yields: 51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) A tuple of name and index. 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """ 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for match in RESOURCE_EXTRACT_REGEX.finditer(string_to_scan): 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) yield match.group(1, 2) 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)def _GetResourceListFromString(resources_content): 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Produces a list of |Resource| objects from a string. 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) The input string conaints lines of the form "#define NAME INDEX". The returned 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) list is sorted primarily by hash, then name, and then index. 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Args: 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) resources_content: The input string to process, contains lines of the form 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "#define NAME INDEX". 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Returns: 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) A sorted list of |Resource| objects. 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """ 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) resources = [Resource(_HashName(name), name, index) for name, index in 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) _GetNameIndexPairsIter(resources_content)] 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) # The default |Resource| order makes |resources| sorted by the hash, then 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) # name, then index. 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) resources.sort() 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return resources 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)def _CheckForHashCollisions(sorted_resource_list): 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Checks a sorted list of |Resource| objects for hash collisions. 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Args: 84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) sorted_resource_list: A sorted list of |Resource| objects. 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Returns: 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) A set of all |Resource| objects with collisions. 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """ 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) collisions = set() 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for i in xrange(len(sorted_resource_list) - 1): 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) resource = sorted_resource_list[i] 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) next_resource = sorted_resource_list[i+1] 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if resource.hash == next_resource.hash: 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) collisions.add(resource) 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) collisions.add(next_resource) 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return collisions 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)def _GenDataArray( 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) resources, entry_pattern, array_name, array_type, data_getter): 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Generates a C++ statement defining a literal array containing the hashes. 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Args: 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) resources: A sorted list of |Resource| objects. 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) entry_pattern: A pattern to be used to generate each entry in the array. The 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pattern is expected to have a place for data and one for a comment, in 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) that order. 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) array_name: The name of the array being generated. 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) array_type: The type of the array being generated. 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) data_getter: A function that gets the array data from a |Resource| object. 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Returns: 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) A string containing a C++ statement defining the an array. 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """ 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) lines = [entry_pattern % (data_getter(r), r.name) for r in resources] 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pattern = """const %(type)s %(name)s[] = { 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)%(content)s 119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)""" 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return pattern % {'type': array_type, 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'name': array_name, 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'content': '\n'.join(lines)} 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)def _GenerateFileContent(resources_content): 127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """Generates the .cc content from the given generated_resources.h content. 128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Args: 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) resources_content: The input string to process, contains lines of the form 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "#define NAME INDEX". 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Returns: 134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) .cc file content defining the kResourceHashes and kResourceIndices arrays. 135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) """ 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) hashed_tuples = _GetResourceListFromString(resources_content) 137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) collisions = _CheckForHashCollisions(hashed_tuples) 139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if collisions: 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) error_message = "\n".join( 141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ["hash: %i, name: %s" % (i[0], i[1]) for i in sorted(collisions)]) 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) error_message = ("\nThe following names had hash collisions " 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "(sorted by the hash value):\n%s\n" %(error_message)) 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) raise HashCollisionError(error_message) 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) hashes_array = _GenDataArray( 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) hashed_tuples, " %iU, // %s", 'kResourceHashes', 'uint32_t', 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) operator.attrgetter('hash')) 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) indices_array = _GenDataArray( 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) hashed_tuples, " %s, // %s", 'kResourceIndices', 'int', 151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) operator.attrgetter('index')) 152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return ( 154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "// This file was generated by generate_resources_map.py. Do not edit.\n" 155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "\n\n" 156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "#include " 157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "\"chrome/browser/metrics/variations/generated_resources_map.h\"\n\n" 158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "namespace chrome_variations {\n\n" 1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) "const size_t kNumResources = %i;\n\n" 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "%s" 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "\n" 162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "%s" 163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "\n" 1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) "} // namespace chrome_variations\n") % ( 1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) len(hashed_tuples), hashes_array, indices_array) 166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)def main(resources_file, map_file): 169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) generated_resources_h = "" 170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) with open(resources_file, "r") as resources: 171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) generated_resources_h = resources.read() 172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if len(generated_resources_h) == 0: 174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) raise Error("No content loaded for %s." % (resources_file)) 175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) file_content = _GenerateFileContent(generated_resources_h) 177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) with open(map_file, "w") as generated_file: 179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) generated_file.write(file_content) 180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)if __name__ == '__main__': 183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) sys.exit(main(sys.argv[1], sys.argv[2])) 184