PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/couchjs/scons/scons-local-2.0.1/SCons/Subst.py

http://github.com/cloudant/bigcouch
Python | 904 lines | 832 code | 20 blank | 52 comment | 8 complexity | aaf2646211e9562c3265d4a967cb69b4 MD5 | raw file
Possible License(s): Apache-2.0
  1. """SCons.Subst
  2. SCons string substitution.
  3. """
  4. #
  5. # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining
  8. # a copy of this software and associated documentation files (the
  9. # "Software"), to deal in the Software without restriction, including
  10. # without limitation the rights to use, copy, modify, merge, publish,
  11. # distribute, sublicense, and/or sell copies of the Software, and to
  12. # permit persons to whom the Software is furnished to do so, subject to
  13. # the following conditions:
  14. #
  15. # The above copyright notice and this permission notice shall be included
  16. # in all copies or substantial portions of the Software.
  17. #
  18. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  19. # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  20. # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  21. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  22. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  23. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  24. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  25. __revision__ = "src/engine/SCons/Subst.py 5134 2010/08/16 23:02:40 bdeegan"
  26. import collections
  27. import re
  28. import SCons.Errors
  29. from SCons.Util import is_String, is_Sequence
  30. # Indexed by the SUBST_* constants below.
  31. _strconv = [SCons.Util.to_String_for_subst,
  32. SCons.Util.to_String_for_subst,
  33. SCons.Util.to_String_for_signature]
  34. AllowableExceptions = (IndexError, NameError)
  35. def SetAllowableExceptions(*excepts):
  36. global AllowableExceptions
  37. AllowableExceptions = [_f for _f in excepts if _f]
  38. def raise_exception(exception, target, s):
  39. name = exception.__class__.__name__
  40. msg = "%s `%s' trying to evaluate `%s'" % (name, exception, s)
  41. if target:
  42. raise SCons.Errors.BuildError(target[0], msg)
  43. else:
  44. raise SCons.Errors.UserError(msg)
  45. class Literal(object):
  46. """A wrapper for a string. If you use this object wrapped
  47. around a string, then it will be interpreted as literal.
  48. When passed to the command interpreter, all special
  49. characters will be escaped."""
  50. def __init__(self, lstr):
  51. self.lstr = lstr
  52. def __str__(self):
  53. return self.lstr
  54. def escape(self, escape_func):
  55. return escape_func(self.lstr)
  56. def for_signature(self):
  57. return self.lstr
  58. def is_literal(self):
  59. return 1
  60. class SpecialAttrWrapper(object):
  61. """This is a wrapper for what we call a 'Node special attribute.'
  62. This is any of the attributes of a Node that we can reference from
  63. Environment variable substitution, such as $TARGET.abspath or
  64. $SOURCES[1].filebase. We implement the same methods as Literal
  65. so we can handle special characters, plus a for_signature method,
  66. such that we can return some canonical string during signature
  67. calculation to avoid unnecessary rebuilds."""
  68. def __init__(self, lstr, for_signature=None):
  69. """The for_signature parameter, if supplied, will be the
  70. canonical string we return from for_signature(). Else
  71. we will simply return lstr."""
  72. self.lstr = lstr
  73. if for_signature:
  74. self.forsig = for_signature
  75. else:
  76. self.forsig = lstr
  77. def __str__(self):
  78. return self.lstr
  79. def escape(self, escape_func):
  80. return escape_func(self.lstr)
  81. def for_signature(self):
  82. return self.forsig
  83. def is_literal(self):
  84. return 1
  85. def quote_spaces(arg):
  86. """Generic function for putting double quotes around any string that
  87. has white space in it."""
  88. if ' ' in arg or '\t' in arg:
  89. return '"%s"' % arg
  90. else:
  91. return str(arg)
  92. class CmdStringHolder(collections.UserString):
  93. """This is a special class used to hold strings generated by
  94. scons_subst() and scons_subst_list(). It defines a special method
  95. escape(). When passed a function with an escape algorithm for a
  96. particular platform, it will return the contained string with the
  97. proper escape sequences inserted.
  98. """
  99. def __init__(self, cmd, literal=None):
  100. collections.UserString.__init__(self, cmd)
  101. self.literal = literal
  102. def is_literal(self):
  103. return self.literal
  104. def escape(self, escape_func, quote_func=quote_spaces):
  105. """Escape the string with the supplied function. The
  106. function is expected to take an arbitrary string, then
  107. return it with all special characters escaped and ready
  108. for passing to the command interpreter.
  109. After calling this function, the next call to str() will
  110. return the escaped string.
  111. """
  112. if self.is_literal():
  113. return escape_func(self.data)
  114. elif ' ' in self.data or '\t' in self.data:
  115. return quote_func(self.data)
  116. else:
  117. return self.data
  118. def escape_list(mylist, escape_func):
  119. """Escape a list of arguments by running the specified escape_func
  120. on every object in the list that has an escape() method."""
  121. def escape(obj, escape_func=escape_func):
  122. try:
  123. e = obj.escape
  124. except AttributeError:
  125. return obj
  126. else:
  127. return e(escape_func)
  128. return list(map(escape, mylist))
  129. class NLWrapper(object):
  130. """A wrapper class that delays turning a list of sources or targets
  131. into a NodeList until it's needed. The specified function supplied
  132. when the object is initialized is responsible for turning raw nodes
  133. into proxies that implement the special attributes like .abspath,
  134. .source, etc. This way, we avoid creating those proxies just
  135. "in case" someone is going to use $TARGET or the like, and only
  136. go through the trouble if we really have to.
  137. In practice, this might be a wash performance-wise, but it's a little
  138. cleaner conceptually...
  139. """
  140. def __init__(self, list, func):
  141. self.list = list
  142. self.func = func
  143. def _return_nodelist(self):
  144. return self.nodelist
  145. def _gen_nodelist(self):
  146. mylist = self.list
  147. if mylist is None:
  148. mylist = []
  149. elif not is_Sequence(mylist):
  150. mylist = [mylist]
  151. # The map(self.func) call is what actually turns
  152. # a list into appropriate proxies.
  153. self.nodelist = SCons.Util.NodeList(list(map(self.func, mylist)))
  154. self._create_nodelist = self._return_nodelist
  155. return self.nodelist
  156. _create_nodelist = _gen_nodelist
  157. class Targets_or_Sources(collections.UserList):
  158. """A class that implements $TARGETS or $SOURCES expansions by in turn
  159. wrapping a NLWrapper. This class handles the different methods used
  160. to access the list, calling the NLWrapper to create proxies on demand.
  161. Note that we subclass collections.UserList purely so that the
  162. is_Sequence() function will identify an object of this class as
  163. a list during variable expansion. We're not really using any
  164. collections.UserList methods in practice.
  165. """
  166. def __init__(self, nl):
  167. self.nl = nl
  168. def __getattr__(self, attr):
  169. nl = self.nl._create_nodelist()
  170. return getattr(nl, attr)
  171. def __getitem__(self, i):
  172. nl = self.nl._create_nodelist()
  173. return nl[i]
  174. def __getslice__(self, i, j):
  175. nl = self.nl._create_nodelist()
  176. i = max(i, 0); j = max(j, 0)
  177. return nl[i:j]
  178. def __str__(self):
  179. nl = self.nl._create_nodelist()
  180. return str(nl)
  181. def __repr__(self):
  182. nl = self.nl._create_nodelist()
  183. return repr(nl)
  184. class Target_or_Source(object):
  185. """A class that implements $TARGET or $SOURCE expansions by in turn
  186. wrapping a NLWrapper. This class handles the different methods used
  187. to access an individual proxy Node, calling the NLWrapper to create
  188. a proxy on demand.
  189. """
  190. def __init__(self, nl):
  191. self.nl = nl
  192. def __getattr__(self, attr):
  193. nl = self.nl._create_nodelist()
  194. try:
  195. nl0 = nl[0]
  196. except IndexError:
  197. # If there is nothing in the list, then we have no attributes to
  198. # pass through, so raise AttributeError for everything.
  199. raise AttributeError("NodeList has no attribute: %s" % attr)
  200. return getattr(nl0, attr)
  201. def __str__(self):
  202. nl = self.nl._create_nodelist()
  203. if nl:
  204. return str(nl[0])
  205. return ''
  206. def __repr__(self):
  207. nl = self.nl._create_nodelist()
  208. if nl:
  209. return repr(nl[0])
  210. return ''
  211. class NullNodeList(SCons.Util.NullSeq):
  212. def __call__(self, *args, **kwargs): return ''
  213. def __str__(self): return ''
  214. NullNodesList = NullNodeList()
  215. def subst_dict(target, source):
  216. """Create a dictionary for substitution of special
  217. construction variables.
  218. This translates the following special arguments:
  219. target - the target (object or array of objects),
  220. used to generate the TARGET and TARGETS
  221. construction variables
  222. source - the source (object or array of objects),
  223. used to generate the SOURCES and SOURCE
  224. construction variables
  225. """
  226. dict = {}
  227. if target:
  228. def get_tgt_subst_proxy(thing):
  229. try:
  230. subst_proxy = thing.get_subst_proxy()
  231. except AttributeError:
  232. subst_proxy = thing # probably a string, just return it
  233. return subst_proxy
  234. tnl = NLWrapper(target, get_tgt_subst_proxy)
  235. dict['TARGETS'] = Targets_or_Sources(tnl)
  236. dict['TARGET'] = Target_or_Source(tnl)
  237. # This is a total cheat, but hopefully this dictionary goes
  238. # away soon anyway. We just let these expand to $TARGETS
  239. # because that's "good enough" for the use of ToolSurrogates
  240. # (see test/ToolSurrogate.py) to generate documentation.
  241. dict['CHANGED_TARGETS'] = '$TARGETS'
  242. dict['UNCHANGED_TARGETS'] = '$TARGETS'
  243. else:
  244. dict['TARGETS'] = NullNodesList
  245. dict['TARGET'] = NullNodesList
  246. if source:
  247. def get_src_subst_proxy(node):
  248. try:
  249. rfile = node.rfile
  250. except AttributeError:
  251. pass
  252. else:
  253. node = rfile()
  254. try:
  255. return node.get_subst_proxy()
  256. except AttributeError:
  257. return node # probably a String, just return it
  258. snl = NLWrapper(source, get_src_subst_proxy)
  259. dict['SOURCES'] = Targets_or_Sources(snl)
  260. dict['SOURCE'] = Target_or_Source(snl)
  261. # This is a total cheat, but hopefully this dictionary goes
  262. # away soon anyway. We just let these expand to $TARGETS
  263. # because that's "good enough" for the use of ToolSurrogates
  264. # (see test/ToolSurrogate.py) to generate documentation.
  265. dict['CHANGED_SOURCES'] = '$SOURCES'
  266. dict['UNCHANGED_SOURCES'] = '$SOURCES'
  267. else:
  268. dict['SOURCES'] = NullNodesList
  269. dict['SOURCE'] = NullNodesList
  270. return dict
  271. # Constants for the "mode" parameter to scons_subst_list() and
  272. # scons_subst(). SUBST_RAW gives the raw command line. SUBST_CMD
  273. # gives a command line suitable for passing to a shell. SUBST_SIG
  274. # gives a command line appropriate for calculating the signature
  275. # of a command line...if this changes, we should rebuild.
  276. SUBST_CMD = 0
  277. SUBST_RAW = 1
  278. SUBST_SIG = 2
  279. _rm = re.compile(r'\$[()]')
  280. _remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)')
  281. # Indexed by the SUBST_* constants above.
  282. _regex_remove = [ _rm, None, _remove ]
  283. def _rm_list(list):
  284. #return [ l for l in list if not l in ('$(', '$)') ]
  285. return [l for l in list if not l in ('$(', '$)')]
  286. def _remove_list(list):
  287. result = []
  288. do_append = result.append
  289. for l in list:
  290. if l == '$(':
  291. do_append = lambda x: None
  292. elif l == '$)':
  293. do_append = result.append
  294. else:
  295. do_append(l)
  296. return result
  297. # Indexed by the SUBST_* constants above.
  298. _list_remove = [ _rm_list, None, _remove_list ]
  299. # Regular expressions for splitting strings and handling substitutions,
  300. # for use by the scons_subst() and scons_subst_list() functions:
  301. #
  302. # The first expression compiled matches all of the $-introduced tokens
  303. # that we need to process in some way, and is used for substitutions.
  304. # The expressions it matches are:
  305. #
  306. # "$$"
  307. # "$("
  308. # "$)"
  309. # "$variable" [must begin with alphabetic or underscore]
  310. # "${any stuff}"
  311. #
  312. # The second expression compiled is used for splitting strings into tokens
  313. # to be processed, and it matches all of the tokens listed above, plus
  314. # the following that affect how arguments do or don't get joined together:
  315. #
  316. # " " [white space]
  317. # "non-white-space" [without any dollar signs]
  318. # "$" [single dollar sign]
  319. #
  320. _dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}'
  321. _dollar_exps = re.compile(r'(%s)' % _dollar_exps_str)
  322. _separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str)
  323. # This regular expression is used to replace strings of multiple white
  324. # space characters in the string result from the scons_subst() function.
  325. _space_sep = re.compile(r'[\t ]+(?![^{]*})')
  326. def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
  327. """Expand a string or list containing construction variable
  328. substitutions.
  329. This is the work-horse function for substitutions in file names
  330. and the like. The companion scons_subst_list() function (below)
  331. handles separating command lines into lists of arguments, so see
  332. that function if that's what you're looking for.
  333. """
  334. if isinstance(strSubst, str) and strSubst.find('$') < 0:
  335. return strSubst
  336. class StringSubber(object):
  337. """A class to construct the results of a scons_subst() call.
  338. This binds a specific construction environment, mode, target and
  339. source with two methods (substitute() and expand()) that handle
  340. the expansion.
  341. """
  342. def __init__(self, env, mode, conv, gvars):
  343. self.env = env
  344. self.mode = mode
  345. self.conv = conv
  346. self.gvars = gvars
  347. def expand(self, s, lvars):
  348. """Expand a single "token" as necessary, returning an
  349. appropriate string containing the expansion.
  350. This handles expanding different types of things (strings,
  351. lists, callables) appropriately. It calls the wrapper
  352. substitute() method to re-expand things as necessary, so that
  353. the results of expansions of side-by-side strings still get
  354. re-evaluated separately, not smushed together.
  355. """
  356. if is_String(s):
  357. try:
  358. s0, s1 = s[:2]
  359. except (IndexError, ValueError):
  360. return s
  361. if s0 != '$':
  362. return s
  363. if s1 == '$':
  364. return '$'
  365. elif s1 in '()':
  366. return s
  367. else:
  368. key = s[1:]
  369. if key[0] == '{' or key.find('.') >= 0:
  370. if key[0] == '{':
  371. key = key[1:-1]
  372. try:
  373. s = eval(key, self.gvars, lvars)
  374. except KeyboardInterrupt:
  375. raise
  376. except Exception, e:
  377. if e.__class__ in AllowableExceptions:
  378. return ''
  379. raise_exception(e, lvars['TARGETS'], s)
  380. else:
  381. if key in lvars:
  382. s = lvars[key]
  383. elif key in self.gvars:
  384. s = self.gvars[key]
  385. elif not NameError in AllowableExceptions:
  386. raise_exception(NameError(key), lvars['TARGETS'], s)
  387. else:
  388. return ''
  389. # Before re-expanding the result, handle
  390. # recursive expansion by copying the local
  391. # variable dictionary and overwriting a null
  392. # string for the value of the variable name
  393. # we just expanded.
  394. #
  395. # This could potentially be optimized by only
  396. # copying lvars when s contains more expansions,
  397. # but lvars is usually supposed to be pretty
  398. # small, and deeply nested variable expansions
  399. # are probably more the exception than the norm,
  400. # so it should be tolerable for now.
  401. lv = lvars.copy()
  402. var = key.split('.')[0]
  403. lv[var] = ''
  404. return self.substitute(s, lv)
  405. elif is_Sequence(s):
  406. def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars):
  407. return conv(substitute(l, lvars))
  408. return list(map(func, s))
  409. elif callable(s):
  410. try:
  411. s = s(target=lvars['TARGETS'],
  412. source=lvars['SOURCES'],
  413. env=self.env,
  414. for_signature=(self.mode != SUBST_CMD))
  415. except TypeError:
  416. # This probably indicates that it's a callable
  417. # object that doesn't match our calling arguments
  418. # (like an Action).
  419. if self.mode == SUBST_RAW:
  420. return s
  421. s = self.conv(s)
  422. return self.substitute(s, lvars)
  423. elif s is None:
  424. return ''
  425. else:
  426. return s
  427. def substitute(self, args, lvars):
  428. """Substitute expansions in an argument or list of arguments.
  429. This serves as a wrapper for splitting up a string into
  430. separate tokens.
  431. """
  432. if is_String(args) and not isinstance(args, CmdStringHolder):
  433. args = str(args) # In case it's a UserString.
  434. try:
  435. def sub_match(match):
  436. return self.conv(self.expand(match.group(1), lvars))
  437. result = _dollar_exps.sub(sub_match, args)
  438. except TypeError:
  439. # If the internal conversion routine doesn't return
  440. # strings (it could be overridden to return Nodes, for
  441. # example), then the 1.5.2 re module will throw this
  442. # exception. Back off to a slower, general-purpose
  443. # algorithm that works for all data types.
  444. args = _separate_args.findall(args)
  445. result = []
  446. for a in args:
  447. result.append(self.conv(self.expand(a, lvars)))
  448. if len(result) == 1:
  449. result = result[0]
  450. else:
  451. result = ''.join(map(str, result))
  452. return result
  453. else:
  454. return self.expand(args, lvars)
  455. if conv is None:
  456. conv = _strconv[mode]
  457. # Doing this every time is a bit of a waste, since the Executor
  458. # has typically already populated the OverrideEnvironment with
  459. # $TARGET/$SOURCE variables. We're keeping this (for now), though,
  460. # because it supports existing behavior that allows us to call
  461. # an Action directly with an arbitrary target+source pair, which
  462. # we use in Tool/tex.py to handle calling $BIBTEX when necessary.
  463. # If we dropped that behavior (or found another way to cover it),
  464. # we could get rid of this call completely and just rely on the
  465. # Executor setting the variables.
  466. if 'TARGET' not in lvars:
  467. d = subst_dict(target, source)
  468. if d:
  469. lvars = lvars.copy()
  470. lvars.update(d)
  471. # We're (most likely) going to eval() things. If Python doesn't
  472. # find a __builtins__ value in the global dictionary used for eval(),
  473. # it copies the current global values for you. Avoid this by
  474. # setting it explicitly and then deleting, so we don't pollute the
  475. # construction environment Dictionary(ies) that are typically used
  476. # for expansion.
  477. gvars['__builtins__'] = __builtins__
  478. ss = StringSubber(env, mode, conv, gvars)
  479. result = ss.substitute(strSubst, lvars)
  480. try:
  481. del gvars['__builtins__']
  482. except KeyError:
  483. pass
  484. if is_String(result):
  485. # Remove $(-$) pairs and any stuff in between,
  486. # if that's appropriate.
  487. remove = _regex_remove[mode]
  488. if remove:
  489. result = remove.sub('', result)
  490. if mode != SUBST_RAW:
  491. # Compress strings of white space characters into
  492. # a single space.
  493. result = _space_sep.sub(' ', result).strip()
  494. elif is_Sequence(result):
  495. remove = _list_remove[mode]
  496. if remove:
  497. result = remove(result)
  498. return result
  499. #Subst_List_Strings = {}
  500. def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
  501. """Substitute construction variables in a string (or list or other
  502. object) and separate the arguments into a command list.
  503. The companion scons_subst() function (above) handles basic
  504. substitutions within strings, so see that function instead
  505. if that's what you're looking for.
  506. """
  507. # try:
  508. # Subst_List_Strings[strSubst] = Subst_List_Strings[strSubst] + 1
  509. # except KeyError:
  510. # Subst_List_Strings[strSubst] = 1
  511. # import SCons.Debug
  512. # SCons.Debug.caller_trace(1)
  513. class ListSubber(collections.UserList):
  514. """A class to construct the results of a scons_subst_list() call.
  515. Like StringSubber, this class binds a specific construction
  516. environment, mode, target and source with two methods
  517. (substitute() and expand()) that handle the expansion.
  518. In addition, however, this class is used to track the state of
  519. the result(s) we're gathering so we can do the appropriate thing
  520. whenever we have to append another word to the result--start a new
  521. line, start a new word, append to the current word, etc. We do
  522. this by setting the "append" attribute to the right method so
  523. that our wrapper methods only need ever call ListSubber.append(),
  524. and the rest of the object takes care of doing the right thing
  525. internally.
  526. """
  527. def __init__(self, env, mode, conv, gvars):
  528. collections.UserList.__init__(self, [])
  529. self.env = env
  530. self.mode = mode
  531. self.conv = conv
  532. self.gvars = gvars
  533. if self.mode == SUBST_RAW:
  534. self.add_strip = lambda x: self.append(x)
  535. else:
  536. self.add_strip = lambda x: None
  537. self.in_strip = None
  538. self.next_line()
  539. def expand(self, s, lvars, within_list):
  540. """Expand a single "token" as necessary, appending the
  541. expansion to the current result.
  542. This handles expanding different types of things (strings,
  543. lists, callables) appropriately. It calls the wrapper
  544. substitute() method to re-expand things as necessary, so that
  545. the results of expansions of side-by-side strings still get
  546. re-evaluated separately, not smushed together.
  547. """
  548. if is_String(s):
  549. try:
  550. s0, s1 = s[:2]
  551. except (IndexError, ValueError):
  552. self.append(s)
  553. return
  554. if s0 != '$':
  555. self.append(s)
  556. return
  557. if s1 == '$':
  558. self.append('$')
  559. elif s1 == '(':
  560. self.open_strip('$(')
  561. elif s1 == ')':
  562. self.close_strip('$)')
  563. else:
  564. key = s[1:]
  565. if key[0] == '{' or key.find('.') >= 0:
  566. if key[0] == '{':
  567. key = key[1:-1]
  568. try:
  569. s = eval(key, self.gvars, lvars)
  570. except KeyboardInterrupt:
  571. raise
  572. except Exception, e:
  573. if e.__class__ in AllowableExceptions:
  574. return
  575. raise_exception(e, lvars['TARGETS'], s)
  576. else:
  577. if key in lvars:
  578. s = lvars[key]
  579. elif key in self.gvars:
  580. s = self.gvars[key]
  581. elif not NameError in AllowableExceptions:
  582. raise_exception(NameError(), lvars['TARGETS'], s)
  583. else:
  584. return
  585. # Before re-expanding the result, handle
  586. # recursive expansion by copying the local
  587. # variable dictionary and overwriting a null
  588. # string for the value of the variable name
  589. # we just expanded.
  590. lv = lvars.copy()
  591. var = key.split('.')[0]
  592. lv[var] = ''
  593. self.substitute(s, lv, 0)
  594. self.this_word()
  595. elif is_Sequence(s):
  596. for a in s:
  597. self.substitute(a, lvars, 1)
  598. self.next_word()
  599. elif callable(s):
  600. try:
  601. s = s(target=lvars['TARGETS'],
  602. source=lvars['SOURCES'],
  603. env=self.env,
  604. for_signature=(self.mode != SUBST_CMD))
  605. except TypeError:
  606. # This probably indicates that it's a callable
  607. # object that doesn't match our calling arguments
  608. # (like an Action).
  609. if self.mode == SUBST_RAW:
  610. self.append(s)
  611. return
  612. s = self.conv(s)
  613. self.substitute(s, lvars, within_list)
  614. elif s is None:
  615. self.this_word()
  616. else:
  617. self.append(s)
  618. def substitute(self, args, lvars, within_list):
  619. """Substitute expansions in an argument or list of arguments.
  620. This serves as a wrapper for splitting up a string into
  621. separate tokens.
  622. """
  623. if is_String(args) and not isinstance(args, CmdStringHolder):
  624. args = str(args) # In case it's a UserString.
  625. args = _separate_args.findall(args)
  626. for a in args:
  627. if a[0] in ' \t\n\r\f\v':
  628. if '\n' in a:
  629. self.next_line()
  630. elif within_list:
  631. self.append(a)
  632. else:
  633. self.next_word()
  634. else:
  635. self.expand(a, lvars, within_list)
  636. else:
  637. self.expand(args, lvars, within_list)
  638. def next_line(self):
  639. """Arrange for the next word to start a new line. This
  640. is like starting a new word, except that we have to append
  641. another line to the result."""
  642. collections.UserList.append(self, [])
  643. self.next_word()
  644. def this_word(self):
  645. """Arrange for the next word to append to the end of the
  646. current last word in the result."""
  647. self.append = self.add_to_current_word
  648. def next_word(self):
  649. """Arrange for the next word to start a new word."""
  650. self.append = self.add_new_word
  651. def add_to_current_word(self, x):
  652. """Append the string x to the end of the current last word
  653. in the result. If that is not possible, then just add
  654. it as a new word. Make sure the entire concatenated string
  655. inherits the object attributes of x (in particular, the
  656. escape function) by wrapping it as CmdStringHolder."""
  657. if not self.in_strip or self.mode != SUBST_SIG:
  658. try:
  659. current_word = self[-1][-1]
  660. except IndexError:
  661. self.add_new_word(x)
  662. else:
  663. # All right, this is a hack and it should probably
  664. # be refactored out of existence in the future.
  665. # The issue is that we want to smoosh words together
  666. # and make one file name that gets escaped if
  667. # we're expanding something like foo$EXTENSION,
  668. # but we don't want to smoosh them together if
  669. # it's something like >$TARGET, because then we'll
  670. # treat the '>' like it's part of the file name.
  671. # So for now, just hard-code looking for the special
  672. # command-line redirection characters...
  673. try:
  674. last_char = str(current_word)[-1]
  675. except IndexError:
  676. last_char = '\0'
  677. if last_char in '<>|':
  678. self.add_new_word(x)
  679. else:
  680. y = current_word + x
  681. # We used to treat a word appended to a literal
  682. # as a literal itself, but this caused problems
  683. # with interpreting quotes around space-separated
  684. # targets on command lines. Removing this makes
  685. # none of the "substantive" end-to-end tests fail,
  686. # so we'll take this out but leave it commented
  687. # for now in case there's a problem not covered
  688. # by the test cases and we need to resurrect this.
  689. #literal1 = self.literal(self[-1][-1])
  690. #literal2 = self.literal(x)
  691. y = self.conv(y)
  692. if is_String(y):
  693. #y = CmdStringHolder(y, literal1 or literal2)
  694. y = CmdStringHolder(y, None)
  695. self[-1][-1] = y
  696. def add_new_word(self, x):
  697. if not self.in_strip or self.mode != SUBST_SIG:
  698. literal = self.literal(x)
  699. x = self.conv(x)
  700. if is_String(x):
  701. x = CmdStringHolder(x, literal)
  702. self[-1].append(x)
  703. self.append = self.add_to_current_word
  704. def literal(self, x):
  705. try:
  706. l = x.is_literal
  707. except AttributeError:
  708. return None
  709. else:
  710. return l()
  711. def open_strip(self, x):
  712. """Handle the "open strip" $( token."""
  713. self.add_strip(x)
  714. self.in_strip = 1
  715. def close_strip(self, x):
  716. """Handle the "close strip" $) token."""
  717. self.add_strip(x)
  718. self.in_strip = None
  719. if conv is None:
  720. conv = _strconv[mode]
  721. # Doing this every time is a bit of a waste, since the Executor
  722. # has typically already populated the OverrideEnvironment with
  723. # $TARGET/$SOURCE variables. We're keeping this (for now), though,
  724. # because it supports existing behavior that allows us to call
  725. # an Action directly with an arbitrary target+source pair, which
  726. # we use in Tool/tex.py to handle calling $BIBTEX when necessary.
  727. # If we dropped that behavior (or found another way to cover it),
  728. # we could get rid of this call completely and just rely on the
  729. # Executor setting the variables.
  730. if 'TARGET' not in lvars:
  731. d = subst_dict(target, source)
  732. if d:
  733. lvars = lvars.copy()
  734. lvars.update(d)
  735. # We're (most likely) going to eval() things. If Python doesn't
  736. # find a __builtins__ value in the global dictionary used for eval(),
  737. # it copies the current global values for you. Avoid this by
  738. # setting it explicitly and then deleting, so we don't pollute the
  739. # construction environment Dictionary(ies) that are typically used
  740. # for expansion.
  741. gvars['__builtins__'] = __builtins__
  742. ls = ListSubber(env, mode, conv, gvars)
  743. ls.substitute(strSubst, lvars, 0)
  744. try:
  745. del gvars['__builtins__']
  746. except KeyError:
  747. pass
  748. return ls.data
  749. def scons_subst_once(strSubst, env, key):
  750. """Perform single (non-recursive) substitution of a single
  751. construction variable keyword.
  752. This is used when setting a variable when copying or overriding values
  753. in an Environment. We want to capture (expand) the old value before
  754. we override it, so people can do things like:
  755. env2 = env.Clone(CCFLAGS = '$CCFLAGS -g')
  756. We do this with some straightforward, brute-force code here...
  757. """
  758. if isinstance(strSubst, str) and strSubst.find('$') < 0:
  759. return strSubst
  760. matchlist = ['$' + key, '${' + key + '}']
  761. val = env.get(key, '')
  762. def sub_match(match, val=val, matchlist=matchlist):
  763. a = match.group(1)
  764. if a in matchlist:
  765. a = val
  766. if is_Sequence(a):
  767. return ' '.join(map(str, a))
  768. else:
  769. return str(a)
  770. if is_Sequence(strSubst):
  771. result = []
  772. for arg in strSubst:
  773. if is_String(arg):
  774. if arg in matchlist:
  775. arg = val
  776. if is_Sequence(arg):
  777. result.extend(arg)
  778. else:
  779. result.append(arg)
  780. else:
  781. result.append(_dollar_exps.sub(sub_match, arg))
  782. else:
  783. result.append(arg)
  784. return result
  785. elif is_String(strSubst):
  786. return _dollar_exps.sub(sub_match, strSubst)
  787. else:
  788. return strSubst
  789. # Local Variables:
  790. # tab-width:4
  791. # indent-tabs-mode:nil
  792. # End:
  793. # vim: set expandtab tabstop=4 shiftwidth=4: