generate_clang_module_map.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. #!/usr/bin/env python3
  2. """
  3. Generates a clang module map for a given directory
  4. """
  5. import argparse
  6. import pathlib
  7. import yaml
  8. import sys
  9. def write_file_if_not_same(file_path, content):
  10. try:
  11. with open(file_path, 'r') as f:
  12. if f.read() == content:
  13. return
  14. except FileNotFoundError:
  15. pass
  16. with open(file_path, 'w') as f:
  17. f.write(content)
  18. def main():
  19. parser = argparse.ArgumentParser(
  20. epilog=__doc__,
  21. formatter_class=argparse.RawDescriptionHelpFormatter)
  22. parser.add_argument('directory', help='source directory to generate module map for')
  23. parser.add_argument('--module-name', help='top-level module name')
  24. parser.add_argument('--module-map', required=True, help='output module map file')
  25. parser.add_argument('--vfs-map', required=True, help='output VFS map file')
  26. parser.add_argument('--exclude-files', nargs='*', required=False, help='files to exclude in the module map')
  27. parser.add_argument('--generated-files', nargs='*', help='extra files to include in the module map')
  28. args = parser.parse_args()
  29. root = pathlib.Path(args.directory)
  30. if not root.is_dir():
  31. print(f"Error: {args.directory} is not a directory", file=sys.stderr)
  32. return 1
  33. pathlib.Path(args.module_map).parent.mkdir(parents=True, exist_ok=True)
  34. pathlib.Path(args.vfs_map).parent.mkdir(parents=True, exist_ok=True)
  35. exclude_files = set(args.exclude_files) if args.exclude_files else set()
  36. header_files = [f for f in root.rglob('**/*.h') if f.is_file() and f.name not in exclude_files]
  37. module_name = args.module_name if args.module_name else root.name
  38. module_map = f"module {module_name} {{\n"
  39. for header_file in header_files:
  40. module_map += f" header \"{header_file.relative_to(root)}\"\n"
  41. for generated_file in args.generated_files:
  42. module_map += f" header \"{generated_file}\"\n"
  43. module_map += " requires cplusplus\n"
  44. module_map += " export *\n"
  45. module_map += "}\n"
  46. vfs_map = {
  47. "version": 0,
  48. "use-external-names": False,
  49. "roots": [
  50. {
  51. "name": f"{root}/module.modulemap",
  52. "type": "file",
  53. "external-contents": f"{args.module_map}"
  54. }
  55. ]
  56. }
  57. write_file_if_not_same(args.module_map, module_map)
  58. write_file_if_not_same(args.vfs_map, yaml.dump(vfs_map))
  59. if __name__ == '__main__':
  60. sys.exit(main())