auto-dep.py 8.3 KB

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