Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(384)

Side by Side Diff: scons-2.0.1/engine/SCons/Subst.py

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

Powered by Google App Engine
This is Rietveld 408576698