amalgamate.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import re
  2. import shutil
  3. import os
  4. import sys
  5. import time
  6. from typing import List, Dict
  7. assert os.system("python prebuild.py") == 0
  8. ROOT = 'include/pocketpy'
  9. PUBLIC_HEADERS = ['config.h', 'export.h', 'vmath.h', 'pocketpy.h']
  10. COPYRIGHT = '''/*
  11. * Copyright (c) 2025 blueloveTH
  12. * Distributed Under The MIT License
  13. * https://github.com/pocketpy/pocketpy
  14. */
  15. #define PK_IS_PUBLIC_INCLUDE
  16. '''
  17. def read_file(path):
  18. with open(path, 'rt', encoding='utf-8') as f:
  19. return f.read()
  20. def write_file(path, content):
  21. with open(path, 'wt', encoding='utf-8', newline='\n') as f:
  22. f.write(content)
  23. if os.path.exists('amalgamated'):
  24. shutil.rmtree('amalgamated')
  25. time.sleep(0.5)
  26. os.mkdir('amalgamated')
  27. class Header:
  28. path: str
  29. content: str # header source
  30. dependencies: List[str]
  31. def __init__(self, path: str):
  32. self.path = path
  33. self.dependencies = []
  34. self.content = read_file(f'{ROOT}/{path}')
  35. # process raw content and get dependencies
  36. self.content = self.content.replace('#pragma once', '')
  37. def _replace(m):
  38. path = m.group(1)
  39. if path.startswith('xmacros/'):
  40. return read_file(f'{ROOT}/{path}') + '\n'
  41. if path in PUBLIC_HEADERS:
  42. return '' # remove include
  43. if path != self.path:
  44. self.dependencies.append(path)
  45. return '' # remove include
  46. self.content = re.sub(
  47. r'#include\s+"pocketpy/(.+)"\s*',
  48. _replace,
  49. self.content
  50. )
  51. def __repr__(self):
  52. return f'Header({self.path!r}, dependencies={self.dependencies})'
  53. def text(self):
  54. return f'// {self.path}\n{self.content}\n'
  55. headers: Dict[str, Header] = {}
  56. for entry in os.listdir(ROOT):
  57. if os.path.isdir(f'{ROOT}/{entry}'):
  58. if entry == 'xmacros' or entry in PUBLIC_HEADERS:
  59. continue
  60. files = os.listdir(f'{ROOT}/{entry}')
  61. for file in sorted(files):
  62. assert file.endswith('.h')
  63. if entry in PUBLIC_HEADERS:
  64. continue
  65. headers[f'{entry}/{file}'] = Header(f'{entry}/{file}')
  66. def merge_c_files():
  67. c_files = [COPYRIGHT, '\n', '#include "pocketpy.h"', '\n']
  68. # merge internal headers
  69. internal_h = []
  70. while True:
  71. for h in headers.values():
  72. if not h.dependencies:
  73. break
  74. else:
  75. if headers:
  76. print(headers)
  77. raise RuntimeError("Circular dependencies detected")
  78. break
  79. # print(h.path)
  80. internal_h.append(h.text())
  81. del headers[h.path]
  82. for h2 in headers.values():
  83. h2.dependencies = [d for d in h2.dependencies if d != h.path]
  84. c_files.extend(internal_h)
  85. def _replace(m):
  86. path = m.group(1)
  87. if path.startswith('xmacros/'):
  88. return read_file(f'{ROOT}/{path}') + '\n'
  89. return '' # remove include
  90. for root, _, files in os.walk('src/'):
  91. for file in files:
  92. if file.endswith('.c'):
  93. path = os.path.join(root, file)
  94. c_files.append(f'// {path}\n')
  95. content = read_file(path)
  96. content = re.sub(
  97. r'#include\s+"pocketpy/(.+)"\s*',
  98. _replace,
  99. content,
  100. )
  101. c_files.append(content)
  102. c_files.append('\n')
  103. return ''.join(c_files)
  104. def merge_h_files():
  105. h_files = [COPYRIGHT, '#pragma once']
  106. def _replace(m):
  107. path = m.group(1)
  108. if path.startswith('xmacros/'):
  109. return read_file(f'{ROOT}/{path}') + '\n'
  110. return '' # remove include
  111. for path in PUBLIC_HEADERS:
  112. content = read_file(f'{ROOT}/{path}')
  113. content = content.replace('#pragma once', '')
  114. content = re.sub(
  115. r'#include\s+"pocketpy/(.+)"\s*',
  116. _replace,
  117. content,
  118. )
  119. h_files.append(content)
  120. return '\n'.join(h_files)
  121. write_file('amalgamated/pocketpy.c', merge_c_files())
  122. write_file('amalgamated/pocketpy.h', merge_h_files())
  123. shutil.copy("src2/main.c", "amalgamated/main.c")
  124. if sys.platform in ['linux', 'darwin']:
  125. ok = os.system("gcc -o main amalgamated/pocketpy.c amalgamated/main.c -O1 --std=c11 -lm -ldl -lpthread")
  126. if ok == 0:
  127. print("Test build success!")
  128. print("amalgamated/pocketpy.h")
  129. shutil.copy("amalgamated/pocketpy.h", "plugins/flutter/pocketpy/src/pocketpy.h")
  130. shutil.copy("amalgamated/pocketpy.c", "plugins/flutter/pocketpy/src/pocketpy.c")