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