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