jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1042 lines
27KB

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2006-2010 (ita)
  4. """
  5. C/C++ preprocessor for finding dependencies
  6. Reasons for using the Waf preprocessor by default
  7. #. Some c/c++ extensions (Qt) require a custom preprocessor for obtaining the dependencies (.moc files)
  8. #. Not all compilers provide .d files for obtaining the dependencies (portability)
  9. #. A naive file scanner will not catch the constructs such as "#include foo()"
  10. #. A naive file scanner will catch unnecessary dependencies (change an unused header -> recompile everything)
  11. Regarding the speed concerns:
  12. * the preprocessing is performed only when files must be compiled
  13. * the macros are evaluated only for #if/#elif/#include
  14. * system headers are not scanned by default
  15. Now if you do not want the Waf preprocessor, the tool +gccdeps* uses the .d files produced
  16. during the compilation to track the dependencies (useful when used with the boost libraries).
  17. It only works with gcc >= 4.4 though.
  18. A dumb preprocessor is also available in the tool *c_dumbpreproc*
  19. """
  20. # TODO: more varargs, pragma once
  21. import re, string, traceback
  22. from waflib import Logs, Utils, Errors
  23. from waflib.Logs import debug, error
  24. class PreprocError(Errors.WafError):
  25. pass
  26. POPFILE = '-'
  27. "Constant representing a special token used in :py:meth:`waflib.Tools.c_preproc.c_parser.start` iteration to switch to a header read previously"
  28. recursion_limit = 150
  29. "Limit on the amount of files to read in the dependency scanner"
  30. go_absolute = False
  31. "Set to True to track headers on files in /usr/include, else absolute paths are ignored (but it becomes very slow)"
  32. standard_includes = ['/usr/include']
  33. if Utils.is_win32:
  34. standard_includes = []
  35. use_trigraphs = 0
  36. """Apply trigraph rules (False by default)"""
  37. strict_quotes = 0
  38. """Reserve the "#include <>" quotes for system includes (do not search for those includes). False by default."""
  39. g_optrans = {
  40. 'not':'!',
  41. 'not_eq':'!',
  42. 'and':'&&',
  43. 'and_eq':'&=',
  44. 'or':'||',
  45. 'or_eq':'|=',
  46. 'xor':'^',
  47. 'xor_eq':'^=',
  48. 'bitand':'&',
  49. 'bitor':'|',
  50. 'compl':'~',
  51. }
  52. """Operators such as and/or/xor for c++. Set an empty dict to disable."""
  53. # ignore #warning and #error
  54. re_lines = re.compile(
  55. '^[ \t]*(#|%:)[ \t]*(ifdef|ifndef|if|else|elif|endif|include|import|define|undef|pragma)[ \t]*(.*)\r*$',
  56. re.IGNORECASE | re.MULTILINE)
  57. """Match #include lines"""
  58. re_mac = re.compile("^[a-zA-Z_]\w*")
  59. """Match macro definitions"""
  60. re_fun = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*[(]')
  61. """Match macro functions"""
  62. re_pragma_once = re.compile('^\s*once\s*', re.IGNORECASE)
  63. """Match #pragma once statements"""
  64. re_nl = re.compile('\\\\\r*\n', re.MULTILINE)
  65. """Match newlines"""
  66. re_cpp = re.compile(r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', re.DOTALL | re.MULTILINE )
  67. """Filter C/C++ comments"""
  68. trig_def = [('??'+a, b) for a, b in zip("=-/!'()<>", r'#~\|^[]{}')]
  69. """Trigraph definitions"""
  70. chr_esc = {'0':0, 'a':7, 'b':8, 't':9, 'n':10, 'f':11, 'v':12, 'r':13, '\\':92, "'":39}
  71. """Escape characters"""
  72. NUM = 'i'
  73. """Number token"""
  74. OP = 'O'
  75. """Operator token"""
  76. IDENT = 'T'
  77. """Identifier token"""
  78. STR = 's'
  79. """String token"""
  80. CHAR = 'c'
  81. """Character token"""
  82. tok_types = [NUM, STR, IDENT, OP]
  83. """Token types"""
  84. exp_types = [
  85. r"""0[xX](?P<hex>[a-fA-F0-9]+)(?P<qual1>[uUlL]*)|L*?'(?P<char>(\\.|[^\\'])+)'|(?P<n1>\d+)[Ee](?P<exp0>[+-]*?\d+)(?P<float0>[fFlL]*)|(?P<n2>\d*\.\d+)([Ee](?P<exp1>[+-]*?\d+))?(?P<float1>[fFlL]*)|(?P<n4>\d+\.\d*)([Ee](?P<exp2>[+-]*?\d+))?(?P<float2>[fFlL]*)|(?P<oct>0*)(?P<n0>\d+)(?P<qual2>[uUlL]*)""",
  86. r'L?"([^"\\]|\\.)*"',
  87. r'[a-zA-Z_]\w*',
  88. r'%:%:|<<=|>>=|\.\.\.|<<|<%|<:|<=|>>|>=|\+\+|\+=|--|->|-=|\*=|/=|%:|%=|%>|==|&&|&=|\|\||\|=|\^=|:>|!=|##|[\(\)\{\}\[\]<>\?\|\^\*\+&=:!#;,%/\-\?\~\.]',
  89. ]
  90. """Expression types"""
  91. re_clexer = re.compile('|'.join(["(?P<%s>%s)" % (name, part) for name, part in zip(tok_types, exp_types)]), re.M)
  92. """Match expressions into tokens"""
  93. accepted = 'a'
  94. """Parser state is *accepted*"""
  95. ignored = 'i'
  96. """Parser state is *ignored*, for example preprocessor lines in an #if 0 block"""
  97. undefined = 'u'
  98. """Parser state is *undefined* at the moment"""
  99. skipped = 's'
  100. """Parser state is *skipped*, for example preprocessor lines in a #elif 0 block"""
  101. def repl(m):
  102. """Replace function used with :py:attr:`waflib.Tools.c_preproc.re_cpp`"""
  103. s = m.group(0)
  104. if s.startswith('/'):
  105. return ' '
  106. return s
  107. def filter_comments(filename):
  108. """
  109. Filter the comments from a c/h file, and return the preprocessor lines.
  110. The regexps :py:attr:`waflib.Tools.c_preproc.re_cpp`, :py:attr:`waflib.Tools.c_preproc.re_nl` and :py:attr:`waflib.Tools.c_preproc.re_lines` are used internally.
  111. :return: the preprocessor directives as a list of (keyword, line)
  112. :rtype: a list of string pairs
  113. """
  114. # return a list of tuples : keyword, line
  115. code = Utils.readf(filename)
  116. if use_trigraphs:
  117. for (a, b) in trig_def: code = code.split(a).join(b)
  118. code = re_nl.sub('', code)
  119. code = re_cpp.sub(repl, code)
  120. return [(m.group(2), m.group(3)) for m in re.finditer(re_lines, code)]
  121. prec = {}
  122. """
  123. Operator precendence rules required for parsing expressions of the form::
  124. #if 1 && 2 != 0
  125. """
  126. ops = ['* / %', '+ -', '<< >>', '< <= >= >', '== !=', '& | ^', '&& ||', ',']
  127. for x in range(len(ops)):
  128. syms = ops[x]
  129. for u in syms.split():
  130. prec[u] = x
  131. def trimquotes(s):
  132. """
  133. Remove the single quotes around an expression::
  134. trimquotes("'test'") == "test"
  135. :param s: expression to transform
  136. :type s: string
  137. :rtype: string
  138. """
  139. if not s: return ''
  140. s = s.rstrip()
  141. if s[0] == "'" and s[-1] == "'": return s[1:-1]
  142. return s
  143. def reduce_nums(val_1, val_2, val_op):
  144. """
  145. Apply arithmetic rules to compute a result
  146. :param val1: input parameter
  147. :type val1: int or string
  148. :param val2: input parameter
  149. :type val2: int or string
  150. :param val_op: C operator in *+*, */*, *-*, etc
  151. :type val_op: string
  152. :rtype: int
  153. """
  154. #print val_1, val_2, val_op
  155. # now perform the operation, make certain a and b are numeric
  156. try: a = 0 + val_1
  157. except TypeError: a = int(val_1)
  158. try: b = 0 + val_2
  159. except TypeError: b = int(val_2)
  160. d = val_op
  161. if d == '%': c = a%b
  162. elif d=='+': c = a+b
  163. elif d=='-': c = a-b
  164. elif d=='*': c = a*b
  165. elif d=='/': c = a/b
  166. elif d=='^': c = a^b
  167. elif d=='==': c = int(a == b)
  168. elif d=='|' or d == 'bitor': c = a|b
  169. elif d=='||' or d == 'or' : c = int(a or b)
  170. elif d=='&' or d == 'bitand': c = a&b
  171. elif d=='&&' or d == 'and': c = int(a and b)
  172. elif d=='!=' or d == 'not_eq': c = int(a != b)
  173. elif d=='^' or d == 'xor': c = int(a^b)
  174. elif d=='<=': c = int(a <= b)
  175. elif d=='<': c = int(a < b)
  176. elif d=='>': c = int(a > b)
  177. elif d=='>=': c = int(a >= b)
  178. elif d=='<<': c = a<<b
  179. elif d=='>>': c = a>>b
  180. else: c = 0
  181. return c
  182. def get_num(lst):
  183. """
  184. Try to obtain a number from a list of tokens. The token types are defined in :py:attr:`waflib.Tools.ccroot.tok_types`.
  185. :param lst: list of preprocessor tokens
  186. :type lst: list of tuple (tokentype, value)
  187. :return: a pair containing the number and the rest of the list
  188. :rtype: tuple(value, list)
  189. """
  190. if not lst: raise PreprocError("empty list for get_num")
  191. (p, v) = lst[0]
  192. if p == OP:
  193. if v == '(':
  194. count_par = 1
  195. i = 1
  196. while i < len(lst):
  197. (p, v) = lst[i]
  198. if p == OP:
  199. if v == ')':
  200. count_par -= 1
  201. if count_par == 0:
  202. break
  203. elif v == '(':
  204. count_par += 1
  205. i += 1
  206. else:
  207. raise PreprocError("rparen expected %r" % lst)
  208. (num, _) = get_term(lst[1:i])
  209. return (num, lst[i+1:])
  210. elif v == '+':
  211. return get_num(lst[1:])
  212. elif v == '-':
  213. num, lst = get_num(lst[1:])
  214. return (reduce_nums('-1', num, '*'), lst)
  215. elif v == '!':
  216. num, lst = get_num(lst[1:])
  217. return (int(not int(num)), lst)
  218. elif v == '~':
  219. num, lst = get_num(lst[1:])
  220. return (~ int(num), lst)
  221. else:
  222. raise PreprocError("Invalid op token %r for get_num" % lst)
  223. elif p == NUM:
  224. return v, lst[1:]
  225. elif p == IDENT:
  226. # all macros should have been replaced, remaining identifiers eval to 0
  227. return 0, lst[1:]
  228. else:
  229. raise PreprocError("Invalid token %r for get_num" % lst)
  230. def get_term(lst):
  231. """
  232. Evaluate an expression recursively, for example::
  233. 1+1+1 -> 2+1 -> 3
  234. :param lst: list of tokens
  235. :type lst: list of tuple(token, value)
  236. :return: the value and the remaining tokens
  237. :rtype: value, list
  238. """
  239. if not lst: raise PreprocError("empty list for get_term")
  240. num, lst = get_num(lst)
  241. if not lst:
  242. return (num, [])
  243. (p, v) = lst[0]
  244. if p == OP:
  245. if v == ',':
  246. # skip
  247. return get_term(lst[1:])
  248. elif v == '?':
  249. count_par = 0
  250. i = 1
  251. while i < len(lst):
  252. (p, v) = lst[i]
  253. if p == OP:
  254. if v == ')':
  255. count_par -= 1
  256. elif v == '(':
  257. count_par += 1
  258. elif v == ':':
  259. if count_par == 0:
  260. break
  261. i += 1
  262. else:
  263. raise PreprocError("rparen expected %r" % lst)
  264. if int(num):
  265. return get_term(lst[1:i])
  266. else:
  267. return get_term(lst[i+1:])
  268. else:
  269. num2, lst = get_num(lst[1:])
  270. if not lst:
  271. # no more tokens to process
  272. num2 = reduce_nums(num, num2, v)
  273. return get_term([(NUM, num2)] + lst)
  274. # operator precedence
  275. p2, v2 = lst[0]
  276. if p2 != OP:
  277. raise PreprocError("op expected %r" % lst)
  278. if prec[v2] >= prec[v]:
  279. num2 = reduce_nums(num, num2, v)
  280. return get_term([(NUM, num2)] + lst)
  281. else:
  282. num3, lst = get_num(lst[1:])
  283. num3 = reduce_nums(num2, num3, v2)
  284. return get_term([(NUM, num), (p, v), (NUM, num3)] + lst)
  285. raise PreprocError("cannot reduce %r" % lst)
  286. def reduce_eval(lst):
  287. """
  288. Take a list of tokens and output true or false for #if/#elif conditions.
  289. :param lst: a list of tokens
  290. :type lst: list of tuple(token, value)
  291. :return: a token
  292. :rtype: tuple(NUM, int)
  293. """
  294. num, lst = get_term(lst)
  295. return (NUM, num)
  296. def stringize(lst):
  297. """
  298. Merge a list of tokens into a string
  299. :param lst: a list of tokens
  300. :type lst: list of tuple(token, value)
  301. :rtype: string
  302. """
  303. lst = [str(v2) for (p2, v2) in lst]
  304. return "".join(lst)
  305. def paste_tokens(t1, t2):
  306. """
  307. Token pasting works between identifiers, particular operators, and identifiers and numbers::
  308. a ## b -> ab
  309. > ## = -> >=
  310. a ## 2 -> a2
  311. :param t1: token
  312. :type t1: tuple(type, value)
  313. :param t2: token
  314. :type t2: tuple(type, value)
  315. """
  316. p1 = None
  317. if t1[0] == OP and t2[0] == OP:
  318. p1 = OP
  319. elif t1[0] == IDENT and (t2[0] == IDENT or t2[0] == NUM):
  320. p1 = IDENT
  321. elif t1[0] == NUM and t2[0] == NUM:
  322. p1 = NUM
  323. if not p1:
  324. raise PreprocError('tokens do not make a valid paste %r and %r' % (t1, t2))
  325. return (p1, t1[1] + t2[1])
  326. def reduce_tokens(lst, defs, ban=[]):
  327. """
  328. Replace the tokens in lst, using the macros provided in defs, and a list of macros that cannot be re-applied
  329. :param lst: list of tokens
  330. :type lst: list of tuple(token, value)
  331. :param defs: macro definitions
  332. :type defs: dict
  333. :param ban: macros that cannot be substituted (recursion is not allowed)
  334. :type ban: list of string
  335. :return: the new list of tokens
  336. :rtype: value, list
  337. """
  338. i = 0
  339. while i < len(lst):
  340. (p, v) = lst[i]
  341. if p == IDENT and v == "defined":
  342. del lst[i]
  343. if i < len(lst):
  344. (p2, v2) = lst[i]
  345. if p2 == IDENT:
  346. if v2 in defs:
  347. lst[i] = (NUM, 1)
  348. else:
  349. lst[i] = (NUM, 0)
  350. elif p2 == OP and v2 == '(':
  351. del lst[i]
  352. (p2, v2) = lst[i]
  353. del lst[i] # remove the ident, and change the ) for the value
  354. if v2 in defs:
  355. lst[i] = (NUM, 1)
  356. else:
  357. lst[i] = (NUM, 0)
  358. else:
  359. raise PreprocError("Invalid define expression %r" % lst)
  360. elif p == IDENT and v in defs:
  361. if isinstance(defs[v], str):
  362. a, b = extract_macro(defs[v])
  363. defs[v] = b
  364. macro_def = defs[v]
  365. to_add = macro_def[1]
  366. if isinstance(macro_def[0], list):
  367. # macro without arguments
  368. del lst[i]
  369. accu = to_add[:]
  370. reduce_tokens(accu, defs, ban+[v])
  371. for x in range(len(accu)):
  372. lst.insert(i, accu[x])
  373. i += 1
  374. else:
  375. # collect the arguments for the funcall
  376. args = []
  377. del lst[i]
  378. if i >= len(lst):
  379. raise PreprocError("expected '(' after %r (got nothing)" % v)
  380. (p2, v2) = lst[i]
  381. if p2 != OP or v2 != '(':
  382. raise PreprocError("expected '(' after %r" % v)
  383. del lst[i]
  384. one_param = []
  385. count_paren = 0
  386. while i < len(lst):
  387. p2, v2 = lst[i]
  388. del lst[i]
  389. if p2 == OP and count_paren == 0:
  390. if v2 == '(':
  391. one_param.append((p2, v2))
  392. count_paren += 1
  393. elif v2 == ')':
  394. if one_param: args.append(one_param)
  395. break
  396. elif v2 == ',':
  397. if not one_param: raise PreprocError("empty param in funcall %s" % v)
  398. args.append(one_param)
  399. one_param = []
  400. else:
  401. one_param.append((p2, v2))
  402. else:
  403. one_param.append((p2, v2))
  404. if v2 == '(': count_paren += 1
  405. elif v2 == ')': count_paren -= 1
  406. else:
  407. raise PreprocError('malformed macro')
  408. # substitute the arguments within the define expression
  409. accu = []
  410. arg_table = macro_def[0]
  411. j = 0
  412. while j < len(to_add):
  413. (p2, v2) = to_add[j]
  414. if p2 == OP and v2 == '#':
  415. # stringize is for arguments only
  416. if j+1 < len(to_add) and to_add[j+1][0] == IDENT and to_add[j+1][1] in arg_table:
  417. toks = args[arg_table[to_add[j+1][1]]]
  418. accu.append((STR, stringize(toks)))
  419. j += 1
  420. else:
  421. accu.append((p2, v2))
  422. elif p2 == OP and v2 == '##':
  423. # token pasting, how can man invent such a complicated system?
  424. if accu and j+1 < len(to_add):
  425. # we have at least two tokens
  426. t1 = accu[-1]
  427. if to_add[j+1][0] == IDENT and to_add[j+1][1] in arg_table:
  428. toks = args[arg_table[to_add[j+1][1]]]
  429. if toks:
  430. accu[-1] = paste_tokens(t1, toks[0]) #(IDENT, accu[-1][1] + toks[0][1])
  431. accu.extend(toks[1:])
  432. else:
  433. # error, case "a##"
  434. accu.append((p2, v2))
  435. accu.extend(toks)
  436. elif to_add[j+1][0] == IDENT and to_add[j+1][1] == '__VA_ARGS__':
  437. # TODO not sure
  438. # first collect the tokens
  439. va_toks = []
  440. st = len(macro_def[0])
  441. pt = len(args)
  442. for x in args[pt-st+1:]:
  443. va_toks.extend(x)
  444. va_toks.append((OP, ','))
  445. if va_toks: va_toks.pop() # extra comma
  446. if len(accu)>1:
  447. (p3, v3) = accu[-1]
  448. (p4, v4) = accu[-2]
  449. if v3 == '##':
  450. # remove the token paste
  451. accu.pop()
  452. if v4 == ',' and pt < st:
  453. # remove the comma
  454. accu.pop()
  455. accu += va_toks
  456. else:
  457. accu[-1] = paste_tokens(t1, to_add[j+1])
  458. j += 1
  459. else:
  460. # Invalid paste, case "##a" or "b##"
  461. accu.append((p2, v2))
  462. elif p2 == IDENT and v2 in arg_table:
  463. toks = args[arg_table[v2]]
  464. reduce_tokens(toks, defs, ban+[v])
  465. accu.extend(toks)
  466. else:
  467. accu.append((p2, v2))
  468. j += 1
  469. reduce_tokens(accu, defs, ban+[v])
  470. for x in range(len(accu)-1, -1, -1):
  471. lst.insert(i, accu[x])
  472. i += 1
  473. def eval_macro(lst, defs):
  474. """
  475. Reduce the tokens by :py:func:`waflib.Tools.c_preproc.reduce_tokens` and try to return a 0/1 result by :py:func:`waflib.Tools.c_preproc.reduce_eval`.
  476. :param lst: list of tokens
  477. :type lst: list of tuple(token, value)
  478. :param defs: macro definitions
  479. :type defs: dict
  480. :rtype: int
  481. """
  482. reduce_tokens(lst, defs, [])
  483. if not lst: raise PreprocError("missing tokens to evaluate")
  484. (p, v) = reduce_eval(lst)
  485. return int(v) != 0
  486. def extract_macro(txt):
  487. """
  488. Process a macro definition of the form::
  489. #define f(x, y) x * y
  490. into a function or a simple macro without arguments
  491. :param txt: expression to exact a macro definition from
  492. :type txt: string
  493. :return: a tuple containing the name, the list of arguments and the replacement
  494. :rtype: tuple(string, [list, list])
  495. """
  496. t = tokenize(txt)
  497. if re_fun.search(txt):
  498. p, name = t[0]
  499. p, v = t[1]
  500. if p != OP: raise PreprocError("expected open parenthesis")
  501. i = 1
  502. pindex = 0
  503. params = {}
  504. prev = '('
  505. while 1:
  506. i += 1
  507. p, v = t[i]
  508. if prev == '(':
  509. if p == IDENT:
  510. params[v] = pindex
  511. pindex += 1
  512. prev = p
  513. elif p == OP and v == ')':
  514. break
  515. else:
  516. raise PreprocError("unexpected token (3)")
  517. elif prev == IDENT:
  518. if p == OP and v == ',':
  519. prev = v
  520. elif p == OP and v == ')':
  521. break
  522. else:
  523. raise PreprocError("comma or ... expected")
  524. elif prev == ',':
  525. if p == IDENT:
  526. params[v] = pindex
  527. pindex += 1
  528. prev = p
  529. elif p == OP and v == '...':
  530. raise PreprocError("not implemented (1)")
  531. else:
  532. raise PreprocError("comma or ... expected (2)")
  533. elif prev == '...':
  534. raise PreprocError("not implemented (2)")
  535. else:
  536. raise PreprocError("unexpected else")
  537. #~ print (name, [params, t[i+1:]])
  538. return (name, [params, t[i+1:]])
  539. else:
  540. (p, v) = t[0]
  541. if len(t) > 1:
  542. return (v, [[], t[1:]])
  543. else:
  544. # empty define, assign an empty token
  545. return (v, [[], [('T','')]])
  546. re_include = re.compile('^\s*(<(?P<a>.*)>|"(?P<b>.*)")')
  547. def extract_include(txt, defs):
  548. """
  549. Process a line in the form::
  550. #include foo
  551. :param txt: include line to process
  552. :type txt: string
  553. :param defs: macro definitions
  554. :type defs: dict
  555. :return: the file name
  556. :rtype: string
  557. """
  558. m = re_include.search(txt)
  559. if m:
  560. if m.group('a'): return '<', m.group('a')
  561. if m.group('b'): return '"', m.group('b')
  562. # perform preprocessing and look at the result, it must match an include
  563. toks = tokenize(txt)
  564. reduce_tokens(toks, defs, ['waf_include'])
  565. if not toks:
  566. raise PreprocError("could not parse include %s" % txt)
  567. if len(toks) == 1:
  568. if toks[0][0] == STR:
  569. return '"', toks[0][1]
  570. else:
  571. if toks[0][1] == '<' and toks[-1][1] == '>':
  572. ret = '<', stringize(toks).lstrip('<').rstrip('>')
  573. return ret
  574. raise PreprocError("could not parse include %s." % txt)
  575. def parse_char(txt):
  576. """
  577. Parse a c character
  578. :param txt: character to parse
  579. :type txt: string
  580. :return: a character literal
  581. :rtype: string
  582. """
  583. if not txt: raise PreprocError("attempted to parse a null char")
  584. if txt[0] != '\\':
  585. return ord(txt)
  586. c = txt[1]
  587. if c == 'x':
  588. if len(txt) == 4 and txt[3] in string.hexdigits: return int(txt[2:], 16)
  589. return int(txt[2:], 16)
  590. elif c.isdigit():
  591. if c == '0' and len(txt)==2: return 0
  592. for i in 3, 2, 1:
  593. if len(txt) > i and txt[1:1+i].isdigit():
  594. return (1+i, int(txt[1:1+i], 8))
  595. else:
  596. try: return chr_esc[c]
  597. except KeyError: raise PreprocError("could not parse char literal '%s'" % txt)
  598. def tokenize(s):
  599. """
  600. Convert a string into a list of tokens (shlex.split does not apply to c/c++/d)
  601. :param s: input to tokenize
  602. :type s: string
  603. :return: a list of tokens
  604. :rtype: list of tuple(token, value)
  605. """
  606. return tokenize_private(s)[:] # force a copy of the results
  607. @Utils.run_once
  608. def tokenize_private(s):
  609. ret = []
  610. for match in re_clexer.finditer(s):
  611. m = match.group
  612. for name in tok_types:
  613. v = m(name)
  614. if v:
  615. if name == IDENT:
  616. try:
  617. g_optrans[v];
  618. name = OP
  619. except KeyError:
  620. # c++ specific
  621. if v.lower() == "true":
  622. v = 1
  623. name = NUM
  624. elif v.lower() == "false":
  625. v = 0
  626. name = NUM
  627. elif name == NUM:
  628. if m('oct'): v = int(v, 8)
  629. elif m('hex'): v = int(m('hex'), 16)
  630. elif m('n0'): v = m('n0')
  631. else:
  632. v = m('char')
  633. if v: v = parse_char(v)
  634. else: v = m('n2') or m('n4')
  635. elif name == OP:
  636. if v == '%:': v = '#'
  637. elif v == '%:%:': v = '##'
  638. elif name == STR:
  639. # remove the quotes around the string
  640. v = v[1:-1]
  641. ret.append((name, v))
  642. break
  643. return ret
  644. @Utils.run_once
  645. def define_name(line):
  646. """
  647. :param line: define line
  648. :type line: string
  649. :rtype: string
  650. :return: the define name
  651. """
  652. return re_mac.match(line).group(0)
  653. class c_parser(object):
  654. """
  655. Used by :py:func:`waflib.Tools.c_preproc.scan` to parse c/h files. Note that by default,
  656. only project headers are parsed.
  657. """
  658. def __init__(self, nodepaths=None, defines=None):
  659. self.lines = []
  660. """list of lines read"""
  661. if defines is None:
  662. self.defs = {}
  663. else:
  664. self.defs = dict(defines) # make a copy
  665. self.state = []
  666. self.count_files = 0
  667. self.currentnode_stack = []
  668. self.nodepaths = nodepaths or []
  669. """Include paths"""
  670. self.nodes = []
  671. """List of :py:class:`waflib.Node.Node` found so far"""
  672. self.names = []
  673. """List of file names that could not be matched by any file"""
  674. self.curfile = ''
  675. """Current file"""
  676. self.ban_includes = set([])
  677. """Includes that must not be read (#pragma once)"""
  678. def cached_find_resource(self, node, filename):
  679. """
  680. Find a file from the input directory
  681. :param node: directory
  682. :type node: :py:class:`waflib.Node.Node`
  683. :param filename: header to find
  684. :type filename: string
  685. :return: the node if found, or None
  686. :rtype: :py:class:`waflib.Node.Node`
  687. """
  688. try:
  689. nd = node.ctx.cache_nd
  690. except AttributeError:
  691. nd = node.ctx.cache_nd = {}
  692. tup = (node, filename)
  693. try:
  694. return nd[tup]
  695. except KeyError:
  696. ret = node.find_resource(filename)
  697. if ret:
  698. if getattr(ret, 'children', None):
  699. ret = None
  700. elif ret.is_child_of(node.ctx.bldnode):
  701. tmp = node.ctx.srcnode.search_node(ret.path_from(node.ctx.bldnode))
  702. if tmp and getattr(tmp, 'children', None):
  703. ret = None
  704. nd[tup] = ret
  705. return ret
  706. def tryfind(self, filename):
  707. """
  708. Try to obtain a node from the filename based from the include paths. Will add
  709. the node found to :py:attr:`waflib.Tools.c_preproc.c_parser.nodes` or the file name to
  710. :py:attr:`waflib.Tools.c_preproc.c_parser.names` if no corresponding file is found. Called by
  711. :py:attr:`waflib.Tools.c_preproc.c_parser.start`.
  712. :param filename: header to find
  713. :type filename: string
  714. :return: the node if found
  715. :rtype: :py:class:`waflib.Node.Node`
  716. """
  717. if filename.endswith('.moc'):
  718. # we could let the qt4 module use a subclass, but then the function "scan" below must be duplicated
  719. # in the qt4 and in the qt5 classes. So we have two lines here and it is sufficient. TODO waf 1.9
  720. self.names.append(filename)
  721. return None
  722. self.curfile = filename
  723. # for msvc it should be a for loop over the whole stack
  724. found = self.cached_find_resource(self.currentnode_stack[-1], filename)
  725. for n in self.nodepaths:
  726. if found:
  727. break
  728. found = self.cached_find_resource(n, filename)
  729. if found and not found in self.ban_includes:
  730. # TODO duplicates do not increase the no-op build times too much, but they may be worth removing
  731. self.nodes.append(found)
  732. self.addlines(found)
  733. else:
  734. if not filename in self.names:
  735. self.names.append(filename)
  736. return found
  737. def addlines(self, node):
  738. """
  739. Add the lines from a header in the list of preprocessor lines to parse
  740. :param node: header
  741. :type node: :py:class:`waflib.Node.Node`
  742. """
  743. self.currentnode_stack.append(node.parent)
  744. filepath = node.abspath()
  745. self.count_files += 1
  746. if self.count_files > recursion_limit:
  747. # issue #812
  748. raise PreprocError("recursion limit exceeded")
  749. pc = self.parse_cache
  750. debug('preproc: reading file %r', filepath)
  751. try:
  752. lns = pc[filepath]
  753. except KeyError:
  754. pass
  755. else:
  756. self.lines.extend(lns)
  757. return
  758. try:
  759. lines = filter_comments(filepath)
  760. lines.append((POPFILE, ''))
  761. lines.reverse()
  762. pc[filepath] = lines # cache the lines filtered
  763. self.lines.extend(lines)
  764. except IOError:
  765. raise PreprocError("could not read the file %s" % filepath)
  766. except Exception:
  767. if Logs.verbose > 0:
  768. error("parsing %s failed" % filepath)
  769. traceback.print_exc()
  770. def start(self, node, env):
  771. """
  772. Preprocess a source file to obtain the dependencies, which are accumulated to :py:attr:`waflib.Tools.c_preproc.c_parser.nodes`
  773. and :py:attr:`waflib.Tools.c_preproc.c_parser.names`.
  774. :param node: source file
  775. :type node: :py:class:`waflib.Node.Node`
  776. :param env: config set containing additional defines to take into account
  777. :type env: :py:class:`waflib.ConfigSet.ConfigSet`
  778. """
  779. debug('preproc: scanning %s (in %s)', node.name, node.parent.name)
  780. bld = node.ctx
  781. try:
  782. self.parse_cache = bld.parse_cache
  783. except AttributeError:
  784. self.parse_cache = bld.parse_cache = {}
  785. self.current_file = node
  786. self.addlines(node)
  787. # macros may be defined on the command-line, so they must be parsed as if they were part of the file
  788. if env['DEFINES']:
  789. try:
  790. lst = ['%s %s' % (x[0], trimquotes('='.join(x[1:]))) for x in [y.split('=') for y in env['DEFINES']]]
  791. lst.reverse()
  792. self.lines.extend([('define', x) for x in lst])
  793. except AttributeError:
  794. # if the defines are invalid the compiler will tell the user
  795. pass
  796. while self.lines:
  797. (token, line) = self.lines.pop()
  798. if token == POPFILE:
  799. self.count_files -= 1
  800. self.currentnode_stack.pop()
  801. continue
  802. try:
  803. ve = Logs.verbose
  804. if ve: debug('preproc: line is %s - %s state is %s', token, line, self.state)
  805. state = self.state
  806. # make certain we define the state if we are about to enter in an if block
  807. if token[:2] == 'if':
  808. state.append(undefined)
  809. elif token == 'endif':
  810. state.pop()
  811. # skip lines when in a dead 'if' branch, wait for the endif
  812. if token[0] != 'e':
  813. if skipped in self.state or ignored in self.state:
  814. continue
  815. if token == 'if':
  816. ret = eval_macro(tokenize(line), self.defs)
  817. if ret: state[-1] = accepted
  818. else: state[-1] = ignored
  819. elif token == 'ifdef':
  820. m = re_mac.match(line)
  821. if m and m.group(0) in self.defs: state[-1] = accepted
  822. else: state[-1] = ignored
  823. elif token == 'ifndef':
  824. m = re_mac.match(line)
  825. if m and m.group(0) in self.defs: state[-1] = ignored
  826. else: state[-1] = accepted
  827. elif token == 'include' or token == 'import':
  828. (kind, inc) = extract_include(line, self.defs)
  829. if ve: debug('preproc: include found %s (%s) ', inc, kind)
  830. if kind == '"' or not strict_quotes:
  831. self.current_file = self.tryfind(inc)
  832. if token == 'import':
  833. self.ban_includes.add(self.current_file)
  834. elif token == 'elif':
  835. if state[-1] == accepted:
  836. state[-1] = skipped
  837. elif state[-1] == ignored:
  838. if eval_macro(tokenize(line), self.defs):
  839. state[-1] = accepted
  840. elif token == 'else':
  841. if state[-1] == accepted: state[-1] = skipped
  842. elif state[-1] == ignored: state[-1] = accepted
  843. elif token == 'define':
  844. try:
  845. self.defs[define_name(line)] = line
  846. except Exception:
  847. raise PreprocError("Invalid define line %s" % line)
  848. elif token == 'undef':
  849. m = re_mac.match(line)
  850. if m and m.group(0) in self.defs:
  851. self.defs.__delitem__(m.group(0))
  852. #print "undef %s" % name
  853. elif token == 'pragma':
  854. if re_pragma_once.match(line.lower()):
  855. self.ban_includes.add(self.current_file)
  856. except Exception as e:
  857. if Logs.verbose:
  858. debug('preproc: line parsing failed (%s): %s %s', e, line, Utils.ex_stack())
  859. def scan(task):
  860. """
  861. Get the dependencies using a c/c++ preprocessor, this is required for finding dependencies of the kind::
  862. #include some_macro()
  863. This function is bound as a task method on :py:class:`waflib.Tools.c.c` and :py:class:`waflib.Tools.cxx.cxx` for example
  864. """
  865. global go_absolute
  866. try:
  867. incn = task.generator.includes_nodes
  868. except AttributeError:
  869. raise Errors.WafError('%r is missing a feature such as "c", "cxx" or "includes": ' % task.generator)
  870. if go_absolute:
  871. nodepaths = incn + [task.generator.bld.root.find_dir(x) for x in standard_includes]
  872. else:
  873. nodepaths = [x for x in incn if x.is_child_of(x.ctx.srcnode) or x.is_child_of(x.ctx.bldnode)]
  874. tmp = c_parser(nodepaths)
  875. tmp.start(task.inputs[0], task.env)
  876. if Logs.verbose:
  877. debug('deps: deps for %r: %r; unresolved %r' % (task.inputs, tmp.nodes, tmp.names))
  878. return (tmp.nodes, tmp.names)