blob: c13faa6453c2e8eeb9944bc163b182dda86a53fe [file] [edit]
#!/usr/bin/env python3
# Copyright (C) 2026 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Generate a clang header map (.hmap) for a list of include directories.
This script builds a JSON manifest and passes it off to LLVM's hmaptool.
On basename collisions the first directory listed wins, matching -I search order.
"""
import argparse
import json
import os
import subprocess
import sys
HEADER_EXTENSIONS = ('.h', '.hh', '.hpp', '.hxx', '.inc', '.def')
HMAPTOOL = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'hmaptool')
def collect_mappings(directories):
mappings = {}
lowercase_owners = {}
for directory in directories:
directory = os.path.abspath(directory)
if not os.path.isdir(directory):
continue
for entry in sorted(os.scandir(directory), key=lambda e: e.name):
if not entry.name.endswith(HEADER_EXTENSIONS):
continue
if entry.name in mappings or not entry.is_file():
continue
key = entry.name.lower()
owner = lowercase_owners.get(key)
if owner and owner != entry.name:
sys.stderr.write(
'generate-header-map: warning: case-insensitive collision between {!r} and {!r}; '
'clang header maps are case-insensitive, so one will shadow the other\n'.format(
mappings[owner], entry.path))
lowercase_owners.setdefault(key, entry.name)
mappings[entry.name] = entry.path
return mappings
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('-o', '--output', required=True, help='output .hmap path')
parser.add_argument('--dirs-file', required=True,
help='file containing one include directory per line')
args = parser.parse_args()
with open(args.dirs_file) as f:
directories = [line.strip() for line in f if line.strip()]
manifest = args.output + '.json'
with open(manifest, 'w') as f:
json.dump({'mappings': collect_mappings(directories)}, f, indent=2, sort_keys=True)
return subprocess.call([sys.executable, HMAPTOOL, 'write', manifest, args.output])
if __name__ == '__main__':
sys.exit(main())