1 """SCons.Subst
2
3 SCons string substitution.
4
5 """
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
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
56
64
65
66
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."""
74
77
78 - def escape(self, escape_func):
79 return escape_func(self.lstr)
80
83
86
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
108
109 - def escape(self, escape_func):
110 return escape_func(self.lstr)
111
114
117
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
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 """
136
139
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
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
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
183 self.list = list
184 self.func = func
188 list = self.list
189 if list is None:
190 list = []
191 elif not is_Sequence(list):
192 list = [list]
193
194
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
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 """
214 nl = self.nl._create_nodelist()
215 return getattr(nl, attr)
217 nl = self.nl._create_nodelist()
218 return nl[i]
220 nl = self.nl._create_nodelist()
221 i = max(i, 0); j = max(j, 0)
222 return nl[i:j]
224 nl = self.nl._create_nodelist()
225 return str(nl)
227 nl = self.nl._create_nodelist()
228 return repr(nl)
229
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 """
239 nl = self.nl._create_nodelist()
240 try:
241 nl0 = nl[0]
242 except IndexError:
243
244
245 raise AttributeError, "NodeList has no attribute: %s" % attr
246 return getattr(nl0, attr)
248 nl = self.nl._create_nodelist()
249 if nl:
250 return str(nl[0])
251 return ''
253 nl = self.nl._create_nodelist()
254 if nl:
255 return repr(nl[0])
256 return ''
257
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
301
302
303
304
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
313 _regex_remove = [ _rm, None, _remove ]
314
316
317 return filter(lambda l: not l in ('$(', '$)'), list)
318
330
331
332 _list_remove = [ _rm_list, None, _remove_list ]
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
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
360
361 _space_sep = re.compile(r'[\t ]+(?![^{]*})')