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