auto-dep.krk 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #!/bin/kuroko
  2. import os, kuroko, fileio
  3. let cflags = "-O2 -g -I. -Iapps -fplan9-extensions -Wall -Wextra -Wno-unused-parameter"
  4. def basename(path: str) -> str:
  5. return path.strip('/').split('/')[-1]
  6. class Classifier:
  7. dependency_hints = {
  8. # Toaru Standard Library
  9. '<toaru/kbd.h>': (None, '-ltoaru_kbd', []),
  10. '<toaru/list.h>': (None, '-ltoaru_list', []),
  11. '<toaru/hashmap.h>': (None, '-ltoaru_hashmap', ['<toaru/list.h>']),
  12. '<toaru/tree.h>': (None, '-ltoaru_tree', ['<toaru/list.h>']),
  13. '<toaru/pex.h>': (None, '-ltoaru_pex', []),
  14. '<toaru/auth.h>': (None, '-ltoaru_auth', []),
  15. '<toaru/graphics.h>': (None, '-ltoaru_graphics', []),
  16. '<toaru/inflate.h>': (None, '-ltoaru_inflate', []),
  17. '<toaru/drawstring.h>': (None, '-ltoaru_drawstring', ['<toaru/graphics.h>']),
  18. '<toaru/jpeg.h>': (None, '-ltoaru_jpeg', ['<toaru/graphics.h>']),
  19. '<toaru/png.h>': (None, '-ltoaru_png', ['<toaru/graphics.h>','<toaru/inflate.h>']),
  20. '<toaru/rline.h>': (None, '-ltoaru_rline', ['<toaru/kbd.h>']),
  21. '<toaru/confreader.h>': (None, '-ltoaru_confreader', ['<toaru/hashmap.h>']),
  22. '<toaru/markup.h>': (None, '-ltoaru_markup', ['<toaru/hashmap.h>']),
  23. '<toaru/json.h>': (None, '-ltoaru_json', ['<toaru/hashmap.h>']),
  24. '<toaru/yutani.h>': (None, '-ltoaru_yutani', ['<toaru/kbd.h>', '<toaru/list.h>', '<toaru/pex.h>', '<toaru/graphics.h>', '<toaru/hashmap.h>']),
  25. '<toaru/decorations.h>': (None, '-ltoaru_decorations', ['<toaru/menu.h>', '<toaru/text.h>', '<toaru/graphics.h>', '<toaru/yutani.h>']),
  26. '<toaru/termemu.h>': (None, '-ltoaru_termemu', ['<toaru/graphics.h>']),
  27. '<toaru/icon_cache.h>': (None, '-ltoaru_icon_cache', ['<toaru/graphics.h>', '<toaru/hashmap.h>']),
  28. '<toaru/menu.h>': (None, '-ltoaru_menu', ['<toaru/yutani.h>', '<toaru/icon_cache.h>', '<toaru/graphics.h>', '<toaru/hashmap.h>']),
  29. '<toaru/button.h>': (None, '-ltoaru_button', ['<toaru/graphics.h>','<toaru/text.h>', '<toaru/icon_cache.h>']),
  30. '<toaru/text.h>': (None, '-ltoaru_text', ['<toaru/graphics.h>', '<toaru/hashmap.h>']),
  31. '<toaru/markup_text.h>': (None, '-ltoaru_markup_text', ['<toaru/graphics.h>', '<toaru/markup.h>', '<toaru/text.h>']),
  32. # Kuroko
  33. '<kuroko/kuroko.h>': ('../../../kuroko/src', '-lkuroko', []),
  34. }
  35. def __init__(self, filename: str):
  36. self.export_dynamic_hint = False
  37. self.filename = filename
  38. self.includes, self.libs = self._depends()
  39. def _calculate(self, depends, new):
  40. """Calculate all dependencies for the given set of new elements."""
  41. for k in new:
  42. if not k in depends:
  43. depends.append(k)
  44. let other = self.dependency_hints[k][2]
  45. depends = self._calculate(depends, other)
  46. return depends
  47. def _sort(self, depends: list[str]) -> list[str]:
  48. """Sort the list of dependencies so that elements appearing first depend on elements following."""
  49. let satisfied = []
  50. let a = depends[:]
  51. while set(satisfied) != set(depends):
  52. let b = []
  53. for k in a:
  54. if all(x in satisfied for x in self.dependency_hints[k][2]):
  55. satisfied.append(k)
  56. else:
  57. b.append(k)
  58. a = b[:]
  59. return reversed(satisfied)
  60. def _depends(self) -> (list[str],list[str]):
  61. """Calculate include and library dependencies."""
  62. let lines = []
  63. let depends = []
  64. with fileio.open(self.filename,'r') as f:
  65. lines = f.readlines()
  66. for l in lines:
  67. if l.startswith('#include'):
  68. depends.extend(k for k in list(self.dependency_hints.keys()) if l.startswith('#include ' + k))
  69. elif l.startswith('/* auto-dep: export-dynamic */'):
  70. self.export_dynamic_hint = True
  71. depends = self._calculate([], depends)
  72. depends = self._sort(depends)
  73. let includes = []
  74. let libraries = []
  75. for k in depends:
  76. let dep = self.dependency_hints[k]
  77. if dep[0]:
  78. includes.append('-I' + 'base/usr/include/' + dep[0])
  79. if dep[1]:
  80. libraries.append(dep[1])
  81. return includes, libraries
  82. def todep(name: str) -> (bool, str):
  83. """Convert a library name to an archive path or object file name."""
  84. if name.startswith("-l"):
  85. name = name.replace("-l","",1)
  86. if name.startswith('toaru'):
  87. return (True, "{}/lib{}.so".format('base/lib', name))
  88. elif name.startswith('kuroko'):
  89. return (True, "{}/lib{}.so".format('base/lib', name))
  90. else:
  91. return (True, "{}/lib{}.so".format('base/usr/lib', name))
  92. else:
  93. return (False, name)
  94. def toheader(name: str) -> str:
  95. if name.startswith('-ltoaru_'):
  96. return name.replace('-ltoaru_','base/usr/include/toaru/') + '.h'
  97. else:
  98. return ''
  99. if __name__ == "__main__":
  100. if len(kuroko.argv) < 3:
  101. print("usage: util/auto-dep.krk command filename")
  102. return 1
  103. let command = kuroko.argv[1]
  104. let filename = kuroko.argv[2]
  105. let c = Classifier(filename)
  106. if command == "--cflags":
  107. print(" ".join([x for x in c.includes]))
  108. elif command == "--libs":
  109. print(" ".join([x for x in c.libs]))
  110. elif command == "--deps":
  111. let results = [todep(x) for x in c.libs]
  112. let normal = [x[1] for x in results if not x[0]]
  113. let order_only = [x[1] for x in results if x[0]]
  114. print(" ".join(normal) + " | " + " ".join(order_only))
  115. elif command == "--build":
  116. os.system("gcc {cflags} {extra} {includes} -o {app} {source} {libraries}".format(
  117. cflags=cflags,
  118. app=basename(filename).replace('.c++','').replace(".c",""),
  119. source=filename,
  120. headers=" ".join([toheader(x) for x in c.libs]),
  121. libraries=" ".join([x for x in c.libs]),
  122. includes=" ".join([x for x in c.includes if x is not None]),
  123. extra="-rdynamic" if c.export_dynamic_hint else "",
  124. ))
  125. elif command == "--buildlib":
  126. let libname = basename(filename).replace('.c++','').replace(".c","")
  127. let _libs = [x for x in c.libs if not x.startswith('-ltoaru_') or x.replace("-ltoaru_","") != libname]
  128. os.system("gcc {cflags} {includes} -shared -fPIC -olibtoaru_{lib}.so {source} {libraries}".format(
  129. cflags=cflags,
  130. lib=libname,
  131. source=filename,
  132. headers=" ".join([toheader(x) for x in c.libs]),
  133. libraryfiles=" ".join([todep(x)[1] for x in _libs]),
  134. libraries=" ".join([x for x in _libs]),
  135. includes=" ".join([x for x in c.includes if x is not None])
  136. ))
  137. elif command == "--make":
  138. print("base/bin/{app}: {source} {headers} util/auto-dep.krk | {libraryfiles} $(LC)\n\t{comp} {extra} {includes} -o $@ $< {libraries}".format(
  139. app=basename(filename).replace('.c++','').replace(".c",""),
  140. source=filename,
  141. headers=" ".join([toheader(x) for x in c.libs]),
  142. libraryfiles=" ".join([todep(x)[1] for x in c.libs]),
  143. libraries=" ".join([x for x in c.libs]),
  144. includes=" ".join([x for x in c.includes if x is not None]),
  145. comp="$(CC) $(CFLAGS)" if '.c++' not in filename else "$(CXX) $(CXXFLAGS)",
  146. extra="-rdynamic" if c.export_dynamic_hint else ""
  147. ))
  148. elif command == "--makelib":
  149. let libname = basename(filename).replace('.c++','').replace(".c","")
  150. let _libs = [x for x in c.libs if not x.startswith('-ltoaru_') or x.replace("-ltoaru_","") != libname]
  151. print("base/lib/libtoaru_{lib}.so: {source} {headers} util/auto-dep.krk | {libraryfiles} $(LC)\n\t{comp} {includes} -shared -fPIC -o $@ $< {libraries}".format(
  152. lib=libname,
  153. source=filename,
  154. headers=" ".join([toheader(x) for x in c.libs]),
  155. libraryfiles=" ".join([todep(x)[1] for x in _libs]),
  156. libraries=" ".join([x for x in _libs]),
  157. includes=" ".join([x for x in c.includes if x is not None]),
  158. comp="$(CC) $(CFLAGS)" if '.c++' not in filename else "$(CXX) $(CXXFLAGS)",
  159. ))
  160. elif command == "--makekurokomod":
  161. let libname = basename(filename).replace('.c++','').replace(".c","").replace("module_","")
  162. let _libs = [x for x in c.libs if not x.startswith('-ltoaru_') or x.replace("-ltoaru_","") != libname]
  163. print("base/lib/kuroko/{lib}.so: {source} {headers} util/auto-dep.krk | {libraryfiles} $(LC)\n\t$(CC) $(CFLAGS) {includes} -shared -fPIC -o $@ $< {libraries}".format(
  164. lib=libname,
  165. source=filename,
  166. headers=" ".join([toheader(x) for x in c.libs]),
  167. libraryfiles=" ".join([todep(x)[1] for x in _libs]),
  168. libraries=" ".join([x for x in _libs]),
  169. includes=" ".join([x for x in c.includes if x is not None])
  170. ))