| #!/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()) |