gen_header.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. #!/usr/bin/python3
  2. #
  3. # gen_header.py - generate portable header
  4. #
  5. # Copyright (C) 2013 Matthew R. Wette
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU General Public License as
  9. # published by the Free Software Foundation; either version 2 of the
  10. # License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful, but
  13. # WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. # General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, see http://www.gnu.org/licenses.
  19. #
  20. # The GNU General Public License is contained in the file COPYING.
  21. #
  22. # v170626a - mwette@alumni.caltech.edu
  23. import sys, os, re, difflib
  24. import pdb
  25. from gen_utils import replace_in_code, move_if_changed, write_w_fill
  26. defcombos = [
  27. ('and', "__APPLE__", "__i386__"),
  28. ('and', "__APPLE__", "__x86_64__"),
  29. ('or', "__MINGW32__", "__CYGWIN32__", ('and', "_WIN32", "_M_IX86")),
  30. ('and', "__linux__", "__i386__"),
  31. ('and', "__linux__", "__x86_64__"),
  32. ('and', "__linux__", "__powerpc__", ('not', "__powerpc64__")),
  33. ('and', "__linux__", "__powerpc__", "__powerpc64__"),
  34. ('and', "__linux__", "__arm__"),
  35. ('and', "__linux__", "__x390__", "__s390x__"),
  36. ('and', "__linux__", "__mips__"),
  37. ]
  38. rx1 = re.compile(r' *(VG_USERREQ_\w+).*')
  39. rx2 = re.compile(r'#define (\w+)(.*)')
  40. def slurp(filename):
  41. """
  42. Read header file and return list of enums and #defines for user codes.
  43. This will raise exception if file has no enums or defs
  44. """
  45. # Skip to enum, then read enum-guts.
  46. enums = []
  47. f0 = open(filename, 'r')
  48. l0 = f0.readline()
  49. while l0:
  50. if l0.startswith(" enum {"): break
  51. l0 = f0.readline()
  52. l0 = f0.readline()
  53. while l0:
  54. m = rx1.match(l0)
  55. if not m: break
  56. enums.append(m.group(1).strip())
  57. l0 = f0.readline()
  58. # Now process defs.
  59. defs = []
  60. while l0:
  61. if not l0.startswith("#define"):
  62. l0 = f0.readline()
  63. continue
  64. defn = l0
  65. while defn.endswith("\\\n"):
  66. defn = defn.rstrip(" \t\\\n")
  67. defn += " " + f0.readline().lstrip()
  68. if defn.find("VALGRIND_DO_CLIENT_REQUEST") >= 0:
  69. defs.append(defn)
  70. l0 = f0.readline()
  71. f0.close()
  72. return [enums,defs]
  73. def get_enum_values(filename, enumnames):
  74. """
  75. For enums that exist int the provided header file return a dict that
  76. gives values for each enum.
  77. """
  78. f1 = open("_z1.c", 'w')
  79. f1.write("#include <stdio.h>\n")
  80. f1.write("#include \"%s\"\n" % (filename,))
  81. f1.write("int main() {\n")
  82. for e in enumnames:
  83. f1.write(" printf(\"enumd['%s'] = \\\"0x%%x\\\"\\n\", %s);\n" % (e,e))
  84. f1.write("}\n")
  85. f1.close()
  86. # run code and collect
  87. os.system("gcc -I../include -o _z1 _z1.c")
  88. f2 = os.popen("./_z1", 'r')
  89. enumd = {}
  90. l = f2.readline()
  91. while l:
  92. exec(l)
  93. l = f2.readline()
  94. f2.close()
  95. os.system("rm _z1 _z1.c")
  96. return enumd
  97. rx3 = re.compile(r"#define\s+([A-Z0-9_]+)\s*(\(.*)")
  98. rx4 = re.compile(r"#define\s+([A-Za-z0-9_]+)(\([A-Za-z0-9_, ]*\))\s*(.*)$")
  99. def massage_defs(defs, enumd):
  100. """
  101. Given an array of defs, replace lval names and expand enum vals.
  102. """
  103. newdefs = []
  104. enuml = enumd.keys()
  105. for d0 in defs:
  106. m = rx4.match(d0)
  107. if not m:
  108. raise Exception("can't handle " + d0)
  109. if m.group(1).startswith("XPROF_"):
  110. lval = "#define " \
  111. + re.sub("XPROF_", "XPROF_", m.group(1)) + m.group(2)
  112. elif m.group(1).startswith("XP_"):
  113. lval = "#define " \
  114. + re.sub("XP_", "XP_", m.group(1)) + m.group(2)
  115. else:
  116. lval = "#define " + m.group(1) + m.group(2)
  117. rval = m.group(3)
  118. for e in enuml:
  119. if rval.find(e) >= 0:
  120. rval = rval.replace(e, enumd[e])
  121. newdefs.append(lval + " " + rval)
  122. return newdefs
  123. def qr_mindefs(rule, defs=[]):
  124. """
  125. Generate min list of defs to satisfy rule.
  126. Example: ('and', "_x_", "_y_") -> ["_x_", "_y_"]
  127. """
  128. def x_leaf(arg):
  129. return [arg]
  130. def x_and(args):
  131. res = []
  132. for a in args:
  133. res.extend(qr_mindefs(a))
  134. return res
  135. def x_or(args):
  136. return qr_mindefs(args[1])
  137. def x_not(args):
  138. return []
  139. if isinstance(rule, tuple):
  140. if rule[0] == 'and':
  141. return x_and(rule[1:])
  142. elif rule[0] == 'or':
  143. return x_or(rule[1:])
  144. elif rule[0] == 'not':
  145. return x_not(rule[1:])
  146. else:
  147. return x_leaf(rule)
  148. def qr_expand(rule):
  149. """
  150. Expand qualifier to cpp code.
  151. Example: ('and', "_x_", "_y_") -> (defined(_x_) && defined(_y_))
  152. """
  153. def x_leaf(arg):
  154. return "defined(" + arg + ")"
  155. def x_and(args):
  156. res = qr_expand(args[0])
  157. for a in args[1:]:
  158. res = res + " && " + qr_expand(a)
  159. return "(" + res + ")"
  160. def x_or(args):
  161. res = qr_expand(args[0])
  162. for a in args[1:]:
  163. res = res + " || " + qr_expand(a)
  164. return "(" + res + ")"
  165. def x_not(args):
  166. return "!" + qr_expand(args[0])
  167. if isinstance(rule, tuple):
  168. if rule[0] == 'and':
  169. return x_and(rule[1:])
  170. elif rule[0] == 'or':
  171. return x_or(rule[1:])
  172. elif rule[0] == 'not':
  173. return x_not(rule[1:])
  174. else:
  175. return x_leaf(rule)
  176. def collect_qdefs(rexpr, defs):
  177. """
  178. Given all defrules return list of symbols used.
  179. """
  180. if isinstance(rexpr, tuple):
  181. for e in rexpr[1:]:
  182. collect_qdefs(e, defs)
  183. else:
  184. if not rexpr in defs:
  185. defs.append(rexpr)
  186. def expand_defs(defs, qc, aq):
  187. """
  188. defs = #define we want to expand
  189. qc = qualifier combo (needs expand by qr_expand).
  190. aq = all qualifiers
  191. """
  192. f1 = open("_z1.c", 'w')
  193. for q in aq:
  194. f1.write("#undef " + q + "\n")
  195. for q in qr_mindefs(qc):
  196. f1.write("#define " + q + "\n")
  197. f1.write("#include \"valgrind.h\"\n")
  198. #
  199. f1.write('=== BEG ===\n')
  200. for d in defs:
  201. f1.write(re.sub("#define", "Edefine", d) + "\n")
  202. f1.write('=== END ===\n')
  203. f1.close()
  204. #
  205. cc_cmd = "cc"
  206. os.system(cc_cmd + " -E -I../include _z1.c >_z1.E")
  207. #
  208. f2 = open("_z1.E", 'r')
  209. xdefs = []
  210. l0 = f2.readline()
  211. while l0:
  212. if l0.startswith("=== BEG ==="):
  213. l0 = f2.readline()
  214. break
  215. l0 = f2.readline()
  216. while l0:
  217. if l0.startswith('=== END ==='):
  218. break
  219. if l0.startswith('Edefine'):
  220. # clean up ...
  221. l = l0
  222. l = re.sub('Edefine ', '#define ', l)
  223. #l = re.sub('_zzq', '', l)
  224. #l = re.sub('_qzz', '', l)
  225. xdefs.append(l)
  226. l0 = f2.readline()
  227. f2.close()
  228. os.system("rm -f _z1.c _z1.E")
  229. return xdefs
  230. def genxhdr1(stuff, f1):
  231. """
  232. called by fill_in_xxx
  233. """
  234. qcl, xdefd = stuff
  235. nqc = len(qcl)
  236. defl = []
  237. el = ""
  238. for ix in range(nqc):
  239. qcstr = qr_expand(qcl[ix])[1:-1]
  240. xdefs = xdefd[ix]
  241. f1.write("#" + el + "if")
  242. col = 4
  243. parts = qcstr.split()
  244. for p in parts:
  245. col = write_w_fill(f1, col, " " + p, leader = "\t", cc = " \\")
  246. f1.write("\n\n")
  247. el = "el"
  248. for xdef in xdefs:
  249. m = rx4.match(xdef)
  250. if not m: raise Exception("NO MATCH")
  251. lval = "#define " + m.group(1) + m.group(2)
  252. if ix == 0: defl.append(lval)
  253. f1.write(lval + "\t\\\n")
  254. lines = chopup(m.group(3), 60)
  255. n = len(lines)
  256. for i in range(n):
  257. f1.write("\t")
  258. f1.write(lines[i])
  259. if i < n -1: f1.write(" \\")
  260. f1.write("\n")
  261. f1.write("\n")
  262. f1.write("#else\n")
  263. for d in defl:
  264. #f1.write(d + " /* */\n")
  265. f1.write(d + " 0\n")
  266. f1.write('#endif\n')
  267. def chopup(line, mx = 60):
  268. """
  269. Chop up into lines of max length mx (default 60).
  270. Deals with double-quotes.
  271. """
  272. lines = []
  273. ln = len(line)
  274. st = 0
  275. nd = st
  276. while nd < ln:
  277. if st + mx > ln:
  278. lines.append(line[st:])
  279. return lines
  280. nd = st + 60
  281. while line[nd-1] != " ":
  282. nd = nd - 1
  283. if nd == st:
  284. raise Exception("can't handle no spaces")
  285. while line[nd-1] == " ": nd = nd - 1 # back up to no spaces
  286. if line.count("\"", st, nd) % 2 != 0:
  287. #pdb.set_trace()
  288. ix = line.rfind("\"", st, nd)
  289. if ix < 0: raise Exception("chopup: bad line")
  290. if st + ix > mx/2:
  291. nd = ix
  292. else:
  293. # \fix should split quote
  294. nd = ix
  295. lines.append(line[st:nd])
  296. while nd < ln and line[nd] == " ": nd = nd + 1
  297. st = nd
  298. return lines
  299. if True:
  300. enums,defs = slurp("cputildefs.h")
  301. enumd = get_enum_values("cputildefs.h", enums)
  302. defs = massage_defs(defs, enumd)
  303. #
  304. alldefs = []
  305. for r in defcombos:
  306. collect_qdefs(r, alldefs)
  307. #
  308. n = len(defcombos)
  309. xdefd = {}
  310. for i in range(n):
  311. dc = defcombos[i]
  312. xdefs = expand_defs(defs, dc, alldefs)
  313. xdefd[i] = xdefs
  314. fname = "cputil.h"
  315. replace_in_code((defcombos,xdefd), fname, "autocoding", genxhdr1)
  316. move_if_changed(fname)
  317. # --- last line of gen_header.py ---