1# Copyright 2016 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5 6import os 7import struct 8import sys 9 10def Main(args): 11 if len(args) < 4: 12 print( 13 "Usage: %s output.hmap Foo.framework header1.h..." % args[0], 14 file=sys.stderr) 15 return 1 16 17 (out, framework, all_headers) = args[1], args[2], args[3:] 18 19 framework_name = os.path.basename(framework).split('.')[0] 20 all_headers = map(os.path.abspath, all_headers) 21 filelist = {} 22 for header in all_headers: 23 filename = os.path.basename(header) 24 filelist[filename] = header 25 filelist[os.path.join(framework_name, filename)] = header 26 WriteHmap(out, filelist) 27 return 0 28 29 30def NextGreaterPowerOf2(x): 31 return 2**(x).bit_length() 32 33 34def WriteHmap(output_name, filelist): 35 """Generates a header map based on |filelist|. 36 37 Per Mark Mentovai: 38 A header map is structured essentially as a hash table, keyed by names used 39 in #includes, and providing pathnames to the actual files. 40 41 The implementation below and the comment above comes from inspecting: 42 http://www.opensource.apple.com/source/distcc/distcc-2503/distcc_dist/include_server/headermap.py?txt 43 while also looking at the implementation in clang in: 44 https://llvm.org/svn/llvm-project/cfe/trunk/lib/Lex/HeaderMap.cpp 45 """ 46 magic = 1751998832 47 version = 1 48 _reserved = 0 49 count = len(filelist) 50 capacity = NextGreaterPowerOf2(count) 51 strings_offset = 24 + (12 * capacity) 52 max_value_length = len(max(filelist.values(), key=lambda v: len(v))) 53 54 out = open(output_name, 'wb') 55 out.write(struct.pack('<LHHLLLL', magic, version, _reserved, strings_offset, 56 count, capacity, max_value_length)) 57 58 # Create empty hashmap buckets. 59 buckets = [None] * capacity 60 for file, path in filelist.items(): 61 key = 0 62 for c in file: 63 key += ord(c.lower()) * 13 64 65 # Fill next empty bucket. 66 while buckets[key & capacity - 1] is not None: 67 key = key + 1 68 buckets[key & capacity - 1] = (file, path) 69 70 next_offset = 1 71 for bucket in buckets: 72 if bucket is None: 73 out.write(struct.pack('<LLL', 0, 0, 0)) 74 else: 75 (file, path) = bucket 76 key_offset = next_offset 77 prefix_offset = key_offset + len(file) + 1 78 suffix_offset = prefix_offset + len(os.path.dirname(path) + os.sep) + 1 79 next_offset = suffix_offset + len(os.path.basename(path)) + 1 80 out.write(struct.pack('<LLL', key_offset, prefix_offset, suffix_offset)) 81 82 # Pad byte since next offset starts at 1. 83 out.write(struct.pack('<x')) 84 85 for bucket in buckets: 86 if bucket is not None: 87 (file, path) = bucket 88 base = os.path.dirname(path) + os.sep 89 path = os.path.basename(path) 90 file = file.encode('UTF-8') 91 base = base.encode('UTF-8') 92 path = path.encode('UTF-8') 93 out.write(struct.pack('<%ds' % len(file), file)) 94 out.write(struct.pack('<s', b'\0')) 95 out.write(struct.pack('<%ds' % len(base), base)) 96 out.write(struct.pack('<s', b'\0')) 97 out.write(struct.pack('<%ds' % len(path), path)) 98 out.write(struct.pack('<s', b'\0')) 99 100 101if __name__ == '__main__': 102 sys.exit(Main(sys.argv)) 103