headerutils.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. #! /usr/bin/python2
  2. import os.path
  3. import sys
  4. import shlex
  5. import re
  6. import subprocess
  7. import shutil
  8. import pickle
  9. import multiprocessing
  10. def find_pound_include (line, use_outside, use_slash):
  11. inc = re.findall (ur"^\s*#\s*include\s*\"(.+?)\"", line)
  12. if len(inc) == 1:
  13. nm = inc[0]
  14. if use_outside or os.path.exists (nm):
  15. if use_slash or '/' not in nm:
  16. return nm
  17. return ""
  18. def find_system_include (line):
  19. inc = re.findall (ur"^\s*#\s*include\s*<(.+?)>", line)
  20. if len(inc) == 1:
  21. return inc[0]
  22. return ""
  23. def find_pound_define (line):
  24. inc = re.findall (ur"^\s*#\s*define ([A-Za-z0-9_]+)", line)
  25. if len(inc) != 0:
  26. if len(inc) > 1:
  27. print "What? more than 1 match in #define??"
  28. print inc
  29. sys.exit(5)
  30. return inc[0];
  31. return ""
  32. def is_pound_if (line):
  33. inc = re.findall ("^\s*#\s*if\s", line)
  34. if not inc:
  35. inc = re.findall ("^\s*#\s*if[n]?def\s", line)
  36. if inc:
  37. return True
  38. return False
  39. def is_pound_endif (line):
  40. inc = re.findall ("^\s*#\s*endif", line)
  41. if inc:
  42. return True
  43. return False
  44. def find_pound_if (line):
  45. inc = re.findall (ur"^\s*#\s*if\s+(.*)", line)
  46. if len(inc) == 0:
  47. inc = re.findall (ur"^\s*#\s*elif\s+(.*)", line)
  48. if len(inc) > 0:
  49. inc2 = re.findall (ur"defined\s*\((.+?)\)", inc[0])
  50. inc3 = re.findall (ur"defined\s+([a-zA-Z0-9_]+)", inc[0])
  51. for yy in inc3:
  52. inc2.append (yy)
  53. return inc2
  54. else:
  55. inc = re.findall (ur"^\s*#\s*ifdef\s(.*)", line)
  56. if len(inc) == 0:
  57. inc = re.findall (ur"^\s*#\s*ifndef\s(.*)", line)
  58. if len(inc) > 0:
  59. inc2 = re.findall ("[A-Za-z_][A-Za-z_0-9]*", inc[0])
  60. return inc2
  61. if len(inc) == 0:
  62. return list ()
  63. print "WTF. more than one line returned for find_pound_if"
  64. print inc
  65. sys.exit(5)
  66. # IINFO - this is a vector of include information. It consists of 7 elements.
  67. # [0] - base name of the file
  68. # [1] - path leading to this file.
  69. # [2] - orderd list of all headers directly included by this file.
  70. # [3] - Ordered list of any headers included within condionally compiled code.
  71. # headers files are expected to have all includes one level deep due to
  72. # the omnipresent guards at the top of the file.
  73. # [4] - List of all macros which are consumed (used) within this file.
  74. # [5] - list of all macros which may be defined in this file.
  75. # [6] - The source code for this file, if cached.
  76. # [7] - line number info for any headers in the source file. Indexed by base
  77. # name, returning the line the include is on.
  78. empty_iinfo = ("", "", list(), list(), list(), list(), list())
  79. # This function will process a file and extract interesting information.
  80. # DO_MACROS indicates whether macros defined and used should be recorded.
  81. # KEEP_SRC indicates the source for the file should be cached.
  82. def process_include_info (filen, do_macros, keep_src):
  83. header = False
  84. if not os.path.exists (filen):
  85. return empty_iinfo
  86. sfile = open (filen, "r");
  87. data = sfile.readlines()
  88. sfile.close()
  89. # Ignore the initial #ifdef HEADER_H in header files
  90. if filen[-2:] == ".h":
  91. nest = -1
  92. header = True
  93. else:
  94. nest = 0
  95. macout = list ()
  96. macin = list()
  97. incl = list()
  98. cond_incl = list()
  99. src_line = { }
  100. guard = ""
  101. for line in (data):
  102. if is_pound_if (line):
  103. nest += 1
  104. elif is_pound_endif (line):
  105. nest -= 1
  106. nm = find_pound_include (line, True, True)
  107. if nm != "" and nm not in incl and nm[-2:] == ".h":
  108. incl.append (nm)
  109. if nest > 0:
  110. cond_incl.append (nm)
  111. if keep_src:
  112. src_line[nm] = line
  113. continue
  114. if do_macros:
  115. d = find_pound_define (line)
  116. if d:
  117. if d not in macout:
  118. macout.append (d);
  119. continue
  120. d = find_pound_if (line)
  121. if d:
  122. # The first #if in a header file should be the guard
  123. if header and len (d) == 1 and guard == "":
  124. if d[0][-2:] == "_H":
  125. guard = d
  126. else:
  127. guard = "Guess there was no guard..."
  128. else:
  129. for mac in d:
  130. if mac != "defined" and mac not in macin:
  131. macin.append (mac);
  132. if not keep_src:
  133. data = list()
  134. return (os.path.basename (filen), os.path.dirname (filen), incl, cond_incl,
  135. macin, macout, data, src_line)
  136. # Extract header info, but no macros or source code.
  137. def process_ii (filen):
  138. return process_include_info (filen, False, False)
  139. # Extract header information, and collect macro information.
  140. def process_ii_macro (filen):
  141. return process_include_info (filen, True, False)
  142. # Extract header information, cache the source lines.
  143. def process_ii_src (filen):
  144. return process_include_info (filen, False, True)
  145. # Extract header information, coolewc macro info and cache the source lines.
  146. def process_ii_macro_src (filen):
  147. return process_include_info (filen, True, True)
  148. def ii_base (iinfo):
  149. return iinfo[0]
  150. def ii_path (iinfo):
  151. return iinfo[1]
  152. def ii_include_list (iinfo):
  153. return iinfo[2]
  154. def ii_include_list_cond (iinfo):
  155. return iinfo[3]
  156. def ii_include_list_non_cond (iinfo):
  157. l = ii_include_list (iinfo)
  158. for n in ii_include_list_cond (iinfo):
  159. l.remove (n)
  160. return l
  161. def ii_macro_consume (iinfo):
  162. return iinfo[4]
  163. def ii_macro_define (iinfo):
  164. return iinfo[5]
  165. def ii_src (iinfo):
  166. return iinfo[6]
  167. def ii_src_line (iinfo):
  168. return iinfo[7]
  169. def ii_read (fname):
  170. f = open (fname, 'rb')
  171. incl = pickle.load (f)
  172. consumes = pickle.load (f)
  173. defines = pickle.load (f)
  174. obj = (fname,fname,incl,list(), list(), consumes, defines, list(), list())
  175. return obj
  176. def ii_write (fname, obj):
  177. f = open (fname, 'wb')
  178. pickle.dump (obj[2], f)
  179. pickle.dump (obj[4], f)
  180. pickle.dump (obj[5], f)
  181. f.close ()
  182. # execute a system command which returns file names
  183. def execute_command (command):
  184. files = list()
  185. f = os.popen (command)
  186. for x in f:
  187. if x[0:2] == "./":
  188. fn = x.rstrip()[2:]
  189. else:
  190. fn = x.rstrip()
  191. files.append(fn)
  192. return files
  193. # Try to locate a build directory from PATH
  194. def find_gcc_bld_dir (path):
  195. blddir = ""
  196. # Look for blddir/gcc/tm.h
  197. command = "find " + path + " -mindepth 2 -maxdepth 3 -name tm.h"
  198. files = execute_command (command)
  199. for y in files:
  200. p = os.path.dirname (y)
  201. if os.path.basename (p) == "gcc":
  202. blddir = p
  203. break
  204. # If not found, try looking a bit deeper
  205. # Dont look this deep initially because a lot of cross target builds may show
  206. # up in the list before a native build... but those are better than nothing.
  207. if not blddir:
  208. command = "find " + path + " -mindepth 3 -maxdepth 5 -name tm.h"
  209. files = execute_command (command)
  210. for y in files:
  211. p = os.path.dirname (y)
  212. if os.path.basename (p) == "gcc":
  213. blddir = p
  214. break
  215. return blddir
  216. # Find files matching pattern NAME, return in a list.
  217. # CURRENT is True if you want to include the current directory
  218. # DEEPER is True if you want to search 3 levels below the current directory
  219. # any files with testsuite diurectories are ignored
  220. def find_gcc_files (name, current, deeper):
  221. files = list()
  222. command = ""
  223. if current:
  224. if not deeper:
  225. command = "find -maxdepth 1 -name " + name + " -not -path \"./testsuite/*\""
  226. else:
  227. command = "find -maxdepth 4 -name " + name + " -not -path \"./testsuite/*\""
  228. else:
  229. if deeper:
  230. command = "find -maxdepth 4 -mindepth 2 -name " + name + " -not -path \"./testsuite/*\""
  231. if command != "":
  232. files = execute_command (command)
  233. return files
  234. # find the list of unique include names found in a file.
  235. def find_unique_include_list_src (data):
  236. found = list ()
  237. for line in data:
  238. d = find_pound_include (line, True, True)
  239. if d and d not in found and d[-2:] == ".h":
  240. found.append (d)
  241. return found
  242. # find the list of unique include names found in a file.
  243. def find_unique_include_list (filen):
  244. data = open (filen).read().splitlines()
  245. return find_unique_include_list_src (data)
  246. # Create the macin, macout, and incl vectors for a file FILEN.
  247. # macin are the macros that are used in #if* conditional expressions
  248. # macout are the macros which are #defined
  249. # incl is the list of incluide files encountered
  250. # returned as a tuple of the filename followed by the triplet of lists
  251. # (filen, macin, macout, incl)
  252. def create_macro_in_out (filen):
  253. sfile = open (filen, "r");
  254. data = sfile.readlines()
  255. sfile.close()
  256. macout = list ()
  257. macin = list()
  258. incl = list()
  259. for line in (data):
  260. d = find_pound_define (line)
  261. if d != "":
  262. if d not in macout:
  263. macout.append (d);
  264. continue
  265. d = find_pound_if (line)
  266. if len(d) != 0:
  267. for mac in d:
  268. if mac != "defined" and mac not in macin:
  269. macin.append (mac);
  270. continue
  271. nm = find_pound_include (line, True, True)
  272. if nm != "" and nm not in incl:
  273. incl.append (nm)
  274. return (filen, macin, macout, incl)
  275. # create the macro information for filen, and create .macin, .macout, and .incl
  276. # files. Return the created macro tuple.
  277. def create_include_data_files (filen):
  278. macros = create_macro_in_out (filen)
  279. depends = macros[1]
  280. defines = macros[2]
  281. incls = macros[3]
  282. disp_message = filen
  283. if len (defines) > 0:
  284. disp_message = disp_message + " " + str(len (defines)) + " #defines"
  285. dfile = open (filen + ".macout", "w")
  286. for x in defines:
  287. dfile.write (x + "\n")
  288. dfile.close ()
  289. if len (depends) > 0:
  290. disp_message = disp_message + " " + str(len (depends)) + " #if dependencies"
  291. dfile = open (filen + ".macin", "w")
  292. for x in depends:
  293. dfile.write (x + "\n")
  294. dfile.close ()
  295. if len (incls) > 0:
  296. disp_message = disp_message + " " + str(len (incls)) + " #includes"
  297. dfile = open (filen + ".incl", "w")
  298. for x in incls:
  299. dfile.write (x + "\n")
  300. dfile.close ()
  301. return macros
  302. # extract data for include file name_h and enter it into the dictionary.
  303. # this does not change once read in. use_requires is True if you want to
  304. # prime the values with already created .requires and .provides files.
  305. def get_include_data (name_h, use_requires):
  306. macin = list()
  307. macout = list()
  308. incl = list ()
  309. if use_requires and os.path.exists (name_h + ".requires"):
  310. macin = open (name_h + ".requires").read().splitlines()
  311. elif os.path.exists (name_h + ".macin"):
  312. macin = open (name_h + ".macin").read().splitlines()
  313. if use_requires and os.path.exists (name_h + ".provides"):
  314. macout = open (name_h + ".provides").read().splitlines()
  315. elif os.path.exists (name_h + ".macout"):
  316. macout = open (name_h + ".macout").read().splitlines()
  317. if os.path.exists (name_h + ".incl"):
  318. incl = open (name_h + ".incl").read().splitlines()
  319. if len(macin) == 0 and len(macout) == 0 and len(incl) == 0:
  320. return ()
  321. data = ( name_h, macin, macout, incl )
  322. return data
  323. # find FIND in src, and replace it with the list of headers in REPLACE.
  324. # Remove any duplicates of FIND in REPLACE, and if some of the REPLACE
  325. # headers occur earlier in the include chain, leave them.
  326. # Return the new SRC only if anything changed.
  327. def find_replace_include (find, replace, src):
  328. res = list()
  329. seen = { }
  330. anything = False
  331. for line in src:
  332. inc = find_pound_include (line, True, True)
  333. if inc == find:
  334. for y in replace:
  335. if seen.get(y) == None:
  336. res.append("#include \""+y+"\"\n")
  337. seen[y] = True
  338. if y != find:
  339. anything = True
  340. # if find isnt in the replacement list, then we are deleting FIND, so changes.
  341. if find not in replace:
  342. anything = True
  343. else:
  344. if inc in replace:
  345. if seen.get(inc) == None:
  346. res.append (line)
  347. seen[inc] = True
  348. else:
  349. res.append (line)
  350. if (anything):
  351. return res
  352. else:
  353. return list()
  354. # pass in a require and provide dictionary to be read in.
  355. def read_require_provides (require, provide):
  356. if not os.path.exists ("require-provide.master"):
  357. print "require-provide.master file is not available. please run data collection."
  358. sys.exit(1)
  359. incl_list = open("require-provide.master").read().splitlines()
  360. for f in incl_list:
  361. if os.path.exists (f+".requires"):
  362. require[os.path.basename (f)] = open (f + ".requires").read().splitlines()
  363. else:
  364. require[os.path.basename (f)] = list ()
  365. if os.path.exists (f+".provides"):
  366. provide[os.path.basename (f)] = open (f + ".provides").read().splitlines()
  367. else:
  368. provide [os.path.basename (f)] = list ()
  369. def build_include_list (filen):
  370. include_files = list()
  371. sfile = open (filen, "r")
  372. data = sfile.readlines()
  373. sfile.close()
  374. for line in data:
  375. nm = find_pound_include (line, False, False)
  376. if nm != "" and nm[-2:] == ".h":
  377. if nm not in include_files:
  378. include_files.append(nm)
  379. return include_files
  380. def build_reverse_include_list (filen):
  381. include_files = list()
  382. sfile = open (filen, "r")
  383. data = sfile.readlines()
  384. sfile.close()
  385. for line in reversed(data):
  386. nm = find_pound_include (line, False, False)
  387. if nm != "":
  388. if nm not in include_files:
  389. include_files.append(nm)
  390. return include_files
  391. # Get compilation return code, and compensate for a warning that we want to
  392. # consider an error when it comes to inlined templates.
  393. def get_make_rc (rc, output):
  394. rc = rc % 1280
  395. if rc == 0:
  396. # This is not considered an error during compilation of an individual file,
  397. # but it will cause an error during link if it isn't defined. If this
  398. # warning is seen during compiling a file, make it a build error so we
  399. # don't remove the header.
  400. h = re.findall ("warning: inline function.*used but never defined", output)
  401. if len(h) != 0:
  402. rc = 1
  403. return rc;
  404. def get_make_output (build_dir, make_opt):
  405. devnull = open('/dev/null', 'w')
  406. at_a_time = multiprocessing.cpu_count() * 2
  407. make = "make -j"+str(at_a_time)+ " "
  408. if build_dir != "":
  409. command = "cd " + build_dir +"; " + make + make_opt
  410. else:
  411. command = make + make_opt
  412. process = subprocess.Popen(command, stdout=devnull, stderr=subprocess.PIPE, shell=True)
  413. output = process.communicate();
  414. rc = get_make_rc (process.returncode, output[1])
  415. return (rc , output[1])
  416. def spawn_makes (command_list):
  417. devnull = open('/dev/null', 'w')
  418. rc = (0,"", "")
  419. proc_res = list()
  420. text = " Trying target builds : "
  421. for command_pair in command_list:
  422. tname = command_pair[0]
  423. command = command_pair[1]
  424. text += tname + ", "
  425. c = subprocess.Popen(command, bufsize=-1, stdout=devnull, stderr=subprocess.PIPE, shell=True)
  426. proc_res.append ((c, tname))
  427. print text[:-2]
  428. for p in proc_res:
  429. output = p[0].communicate()
  430. ret = (get_make_rc (p[0].returncode, output[1]), output[1], p[1])
  431. if (ret[0] != 0):
  432. # Just record the first one.
  433. if rc[0] == 0:
  434. rc = ret;
  435. return rc
  436. def get_make_output_parallel (targ_list, make_opt, at_a_time):
  437. command = list()
  438. targname = list()
  439. if at_a_time == 0:
  440. at_a_time = multiprocessing.cpu_count() * 2
  441. proc_res = [0] * at_a_time
  442. for x in targ_list:
  443. if make_opt[-2:] == ".o":
  444. s = "cd " + x[1] + "/gcc/; make " + make_opt
  445. else:
  446. s = "cd " + x[1] +"; make " + make_opt
  447. command.append ((x[0],s))
  448. num = len(command)
  449. rc = (0,"", "")
  450. loops = num // at_a_time
  451. if (loops > 0):
  452. for idx in range (loops):
  453. ret = spawn_makes (command[idx*at_a_time:(idx+1)*at_a_time])
  454. if ret[0] != 0:
  455. rc = ret
  456. break
  457. if (rc[0] == 0):
  458. leftover = num % at_a_time
  459. if (leftover > 0):
  460. ret = spawn_makes (command[-leftover:])
  461. if ret[0] != 0:
  462. rc = ret
  463. return rc
  464. def readwholefile (src_file):
  465. sfile = open (src_file, "r")
  466. src_data = sfile.readlines()
  467. sfile.close()
  468. return src_data