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 2928 2008/04/29 22:44:09 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 ]+(?![^{]*})')