• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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