Subst.py.svn-base
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:33k
源码类别:

外挂编程

开发平台:

Windows_Unix

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