Package SCons :: Module Subst
[hide private]
[frames] | no frames]

Source Code for Module SCons.Subst

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