3 Various utility functions go here.
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:
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
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.
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
41 from UserDict import UserDict
47 from UserString import UserString
49 # "Borrowed" from the Python 2.2 UserString module
50 # and modified slightly for use with SCons.
52 def __init__(self, seq):
55 elif isinstance(seq, UserString):
56 self.data = seq.data[:]
59 def __str__(self): return str(self.data)
60 def __repr__(self): return repr(self.data)
61 def __int__(self): return int(self.data)
62 def __long__(self): return long(self.data)
63 def __float__(self): return float(self.data)
64 def __complex__(self): return complex(self.data)
65 def __hash__(self): return hash(self.data)
67 def __cmp__(self, string):
68 if isinstance(string, UserString):
69 return cmp(self.data, string.data)
71 return cmp(self.data, string)
72 def __contains__(self, char):
73 return char in self.data
75 def __len__(self): return len(self.data)
76 def __getitem__(self, index): return self.__class__(self.data[index])
77 def __getslice__(self, start, end):
78 start = max(start, 0); end = max(end, 0)
79 return self.__class__(self.data[start:end])
81 def __add__(self, other):
82 if isinstance(other, UserString):
83 return self.__class__(self.data + other.data)
84 elif is_String(other):
85 return self.__class__(self.data + other)
87 return self.__class__(self.data + str(other))
88 def __radd__(self, other):
90 return self.__class__(other + self.data)
92 return self.__class__(str(other) + self.data)
94 return self.__class__(self.data*n)
101 except AttributeError:
104 for i in xrange(len(lists[0])):
105 result.append(tuple(map(lambda l, i=i: l[i], lists)))
107 __builtin__.zip = zip
110 if _altsep is None and sys.platform == 'win32':
111 # My ActivePython 2.0.1 doesn't set os.altsep! What gives?
114 def rightmost_separator(path, sep, _altsep=_altsep):
116 return max(rfind(path, sep), rfind(path, _altsep))
118 rightmost_separator = string.rfind
120 # First two from the Python Cookbook, just for completeness.
121 # (Yeah, yeah, YAGNI...)
122 def containsAny(str, set):
123 """Check whether sequence str contains ANY of the items in set."""
125 if c in str: return 1
128 def containsAll(str, set):
129 """Check whether sequence str contains ALL of the items in set."""
131 if c not in str: return 0
134 def containsOnly(str, set):
135 """Check whether sequence str contains ONLY items in set."""
137 if c not in set: return 0
141 "Same as os.path.splitext() but faster."
142 sep = rightmost_separator(path, os.sep)
143 dot = string.rfind(path, '.')
144 # An ext is only real if it has at least one non-digit char
145 if dot > sep and not containsOnly(path[dot:], "0123456789."):
146 return path[:dot],path[dot:]
152 Make the drive letter (if any) upper case.
153 This is useful because Windows is inconsitent on the case
154 of the drive letter, which can cause inconsistencies when
155 calculating command signatures.
157 drive, rest = os.path.splitdrive(path)
159 path = string.upper(drive) + rest
163 # Generic convert-to-string functions that abstract away whether or
164 # not the Python we're executing has Unicode support. The wrapper
165 # to_String_for_signature() will use a for_signature() method if the
166 # specified object has one.
168 if hasattr(types, 'UnicodeType'):
170 if isinstance(s, UserString):
174 if t is types.UnicodeType:
181 def to_String_for_signature(obj):
183 f = obj.for_signature
185 return to_String(obj)
189 # Indexed by the SUBST_* constants below.
190 _strconv = [to_String, to_String, to_String_for_signature]
193 """A wrapper for a string. If you use this object wrapped
194 around a string, then it will be interpreted as literal.
195 When passed to the command interpreter, all special
196 characters will be escaped."""
197 def __init__(self, lstr):
203 def escape(self, escape_func):
204 return escape_func(self.lstr)
206 def for_signature(self):
209 def is_literal(self):
212 class SpecialAttrWrapper:
213 """This is a wrapper for what we call a 'Node special attribute.'
214 This is any of the attributes of a Node that we can reference from
215 Environment variable substitution, such as $TARGET.abspath or
216 $SOURCES[1].filebase. We implement the same methods as Literal
217 so we can handle special characters, plus a for_signature method,
218 such that we can return some canonical string during signature
219 calculation to avoid unnecessary rebuilds."""
221 def __init__(self, lstr, for_signature=None):
222 """The for_signature parameter, if supplied, will be the
223 canonical string we return from for_signature(). Else
224 we will simply return lstr."""
227 self.forsig = for_signature
234 def escape(self, escape_func):
235 return escape_func(self.lstr)
237 def for_signature(self):
240 def is_literal(self):
243 class CallableComposite(UserList.UserList):
244 """A simple composite callable class that, when called, will invoke all
245 of its contained callables with the same arguments."""
246 def __call__(self, *args, **kwargs):
247 retvals = map(lambda x, args=args, kwargs=kwargs: apply(x,
251 if self.data and (len(self.data) == len(filter(callable, retvals))):
252 return self.__class__(retvals)
253 return NodeList(retvals)
255 class NodeList(UserList.UserList):
256 """This class is almost exactly like a regular list of Nodes
257 (actually it can hold any object), with one important difference.
258 If you try to get an attribute from this list, it will return that
259 attribute from every item in the list. For example:
261 >>> someList = NodeList([ ' foo ', ' bar ' ])
265 def __nonzero__(self):
266 return len(self.data) != 0
269 return string.join(map(str, self.data))
271 def __getattr__(self, name):
273 # If there is nothing in the list, then we have no attributes to
274 # pass through, so raise AttributeError for everything.
275 raise AttributeError, "NodeList has no attribute: %s" % name
277 # Return a list of the attribute, gotten from every element
279 attrList = map(lambda x, n=name: getattr(x, n), self.data)
281 # Special case. If the attribute is callable, we do not want
282 # to return a list of callables. Rather, we want to return a
283 # single callable that, when called, will invoke the function on
284 # all elements of this list.
285 if self.data and (len(self.data) == len(filter(callable, attrList))):
286 return CallableComposite(attrList)
287 return self.__class__(attrList)
289 _valid_var = re.compile(r'[_a-zA-Z]\w*$')
290 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
292 def is_valid_construction_var(varstr):
293 """Return if the specified string is a legitimate construction
296 return _valid_var.match(varstr)
298 def get_environment_var(varstr):
299 """Given a string, first determine if it looks like a reference
300 to a single environment variable, like "$FOO" or "${FOO}".
301 If so, return that variable with no decorations ("FOO").
302 If not, return None."""
303 mo=_get_env_var.match(to_String(varstr))
313 def quote_spaces(arg):
314 """Generic function for putting double quotes around any string that
315 has white space in it."""
316 if ' ' in arg or '\t' in arg:
321 class CmdStringHolder(UserString):
322 """This is a special class used to hold strings generated by
323 scons_subst() and scons_subst_list(). It defines a special method
324 escape(). When passed a function with an escape algorithm for a
325 particular platform, it will return the contained string with the
326 proper escape sequences inserted.
328 This should really be a subclass of UserString, but that module
329 doesn't exist in Python 1.5.2."""
330 def __init__(self, cmd, literal=None):
331 UserString.__init__(self, cmd)
332 self.literal = literal
334 def is_literal(self):
337 def escape(self, escape_func, quote_func=quote_spaces):
338 """Escape the string with the supplied function. The
339 function is expected to take an arbitrary string, then
340 return it with all special characters escaped and ready
341 for passing to the command interpreter.
343 After calling this function, the next call to str() will
344 return the escaped string.
347 if self.is_literal():
348 return escape_func(self.data)
349 elif ' ' in self.data or '\t' in self.data:
350 return quote_func(self.data)
356 self.__call__ = self.print_it
358 def print_it(self, text, append_newline=1):
359 if append_newline: text = text + '\n'
360 sys.stdout.write(text)
362 def dont_print(self, text, append_newline=1):
365 def set_mode(self, mode):
367 self.__call__ = self.print_it
369 self.__call__ = self.dont_print
371 def escape_list(list, escape_func):
372 """Escape a list of arguments by running the specified escape_func
373 on every object in the list that has an escape() method."""
374 def escape(obj, escape_func=escape_func):
377 except AttributeError:
380 return e(escape_func)
381 return map(escape, list)
384 """A wrapper class that delays turning a list of sources or targets
385 into a NodeList until it's needed. The specified function supplied
386 when the object is initialized is responsible for turning raw nodes
387 into proxies that implement the special attributes like .abspath,
388 .source, etc. This way, we avoid creating those proxies just
389 "in case" someone is going to use $TARGET or the like, and only
390 go through the trouble if we really have to.
392 In practice, this might be a wash performance-wise, but it's a little
393 cleaner conceptually...
396 def __init__(self, list, func):
399 def _create_nodelist(self):
402 except AttributeError:
406 elif not is_List(list):
408 # The map(self.func) call is what actually turns
409 # a list into appropriate proxies.
410 self.nodelist = NodeList(map(self.func, list))
413 class Targets_or_Sources(UserList.UserList):
414 """A class that implements $TARGETS or $SOURCES expansions by in turn
415 wrapping a NLWrapper. This class handles the different methods used
416 to access the list, calling the NLWrapper to create proxies on demand.
418 Note that we subclass UserList.UserList purely so that the is_List()
419 function will identify an object of this class as a list during
420 variable expansion. We're not really using any UserList.UserList
423 def __init__(self, nl):
425 def __getattr__(self, attr):
426 nl = self.nl._create_nodelist()
427 return getattr(nl, attr)
428 def __getitem__(self, i):
429 nl = self.nl._create_nodelist()
431 def __getslice__(self, i, j):
432 nl = self.nl._create_nodelist()
433 i = max(i, 0); j = max(j, 0)
436 nl = self.nl._create_nodelist()
439 nl = self.nl._create_nodelist()
442 class Target_or_Source:
443 """A class that implements $TARGET or $SOURCE expansions by in turn
444 wrapping a NLWrapper. This class handles the different methods used
445 to access an individual proxy Node, calling the NLWrapper to create
448 def __init__(self, nl):
450 def __getattr__(self, attr):
451 nl = self.nl._create_nodelist()
455 # If there is nothing in the list, then we have no attributes to
456 # pass through, so raise AttributeError for everything.
457 raise AttributeError, "NodeList has no attribute: %s" % attr
458 return getattr(nl0, attr)
460 nl = self.nl._create_nodelist()
467 nl = self.nl._create_nodelist()
474 def subst_dict(target, source):
475 """Create a dictionary for substitution of special
476 construction variables.
478 This translates the following special arguments:
480 target - the target (object or array of objects),
481 used to generate the TARGET and TARGETS
482 construction variables
484 source - the source (object or array of objects),
485 used to generate the SOURCES and SOURCE
486 construction variables
491 tnl = NLWrapper(target, lambda x: x.get_subst_proxy())
492 dict['TARGETS'] = Targets_or_Sources(tnl)
493 dict['TARGET'] = Target_or_Source(tnl)
496 def get_src_subst_proxy(node):
499 except AttributeError:
503 return node.get_subst_proxy()
504 snl = NLWrapper(source, get_src_subst_proxy)
505 dict['SOURCES'] = Targets_or_Sources(snl)
506 dict['SOURCE'] = Target_or_Source(snl)
510 # Constants for the "mode" parameter to scons_subst_list() and
511 # scons_subst(). SUBST_RAW gives the raw command line. SUBST_CMD
512 # gives a command line suitable for passing to a shell. SUBST_SIG
513 # gives a command line appropriate for calculating the signature
514 # of a command line...if this changes, we should rebuild.
519 _rm = re.compile(r'\$[()]')
520 _remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)')
522 # Indexed by the SUBST_* constants above.
523 _regex_remove = [ _rm, None, _remove ]
525 # Regular expressions for splitting strings and handling substitutions,
526 # for use by the scons_subst() and scons_subst_list() functions:
528 # The first expression compiled matches all of the $-introduced tokens
529 # that we need to process in some way, and is used for substitutions.
530 # The expressions it matches are:
535 # "$variable" [must begin with alphabetic or underscore]
538 # The second expression compiled is used for splitting strings into tokens
539 # to be processed, and it matches all of the tokens listed above, plus
540 # the following that affect how arguments do or don't get joined together:
543 # "non-white-space" [without any dollar signs]
544 # "$" [single dollar sign]
546 _dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}'
547 _dollar_exps = re.compile(r'(%s)' % _dollar_exps_str)
548 _separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str)
550 # This regular expression is used to replace strings of multiple white
551 # space characters in the string result from the scons_subst() function.
552 _space_sep = re.compile(r'[\t ]+(?![^{]*})')
554 def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=None, conv=None, gvars=None):
555 """Expand a string containing construction variable substitutions.
557 This is the work-horse function for substitutions in file names
558 and the like. The companion scons_subst_list() function (below)
559 handles separating command lines into lists of arguments, so see
560 that function if that's what you're looking for.
562 if type(strSubst) == types.StringType and string.find(strSubst, '$') < 0:
566 """A class to construct the results of a scons_subst() call.
568 This binds a specific construction environment, mode, target and
569 source with two methods (substitute() and expand()) that handle
572 def __init__(self, env, mode, target, source, conv, gvars):
580 def expand_var_once(self, var, lvars):
585 return self.gvars[var]
589 def expand(self, s, lvars):
590 """Expand a single "token" as necessary, returning an
591 appropriate string containing the expansion.
593 This handles expanding different types of things (strings,
594 lists, callables) appropriately. It calls the wrapper
595 substitute() method to re-expand things as necessary, so that
596 the results of expansions of side-by-side strings still get
597 re-evaluated separately, not smushed together.
602 except (IndexError, ValueError):
611 if key[0] == '{' or string.find(key, '.') >= 0:
615 s = eval(key, self.gvars, lvars)
616 except (IndexError, NameError, TypeError):
618 except SyntaxError,e:
620 raise SCons.Errors.BuildError, (self.target[0], "Syntax error `%s' trying to evaluate `%s'" % (e,s))
622 raise SCons.Errors.UserError, "Syntax error `%s' trying to evaluate `%s'" % (e,s)
624 s = self.expand_var_once(key, lvars)
626 # Before re-expanding the result, handle
627 # recursive expansion by copying the local
628 # variable dictionary and overwriting a null
629 # string for the value of the variable name
632 # This could potentially be optimized by only
633 # copying lvars when s contains more expansions,
634 # but lvars is usually supposed to be pretty
635 # small, and deeply nested variable expansions
636 # are probably more the exception than the norm,
637 # so it should be tolerable for now.
639 var = string.split(key, '.')[0]
641 return self.substitute(s, lv)
645 def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars):
646 return conv(substitute(l, lvars))
648 return string.join(r)
651 s = s(target=self.target,
654 for_signature=(self.mode != SUBST_CMD))
656 # This probably indicates that it's a callable
657 # object that doesn't match our calling arguments
660 return self.substitute(s, lvars)
666 def substitute(self, args, lvars):
667 """Substitute expansions in an argument or list of arguments.
669 This serves as a wrapper for splitting up a string into
672 if is_String(args) and not isinstance(args, CmdStringHolder):
674 def sub_match(match, conv=self.conv, expand=self.expand, lvars=lvars):
675 return conv(expand(match.group(1), lvars))
676 result = _dollar_exps.sub(sub_match, args)
678 # If the internal conversion routine doesn't return
679 # strings (it could be overridden to return Nodes, for
680 # example), then the 1.5.2 re module will throw this
681 # exception. Back off to a slower, general-purpose
682 # algorithm that works for all data types.
683 args = _separate_args.findall(args)
686 result.append(self.conv(self.expand(a, lvars)))
688 result = string.join(result, '')
694 return self.expand(args, lvars)
697 dict = subst_dict(target, source)
699 conv = _strconv[mode]
701 gvars = env.Dictionary()
703 # We're (most likely) going to eval() things. If Python doesn't
704 # find a __builtin__ value in the global dictionary used for eval(),
705 # it copies the current __builtin__ values for you. Avoid this by
706 # setting it explicitly and then deleting, so we don't pollute the
707 # construction environment Dictionary(ies) that are typically used
709 gvars['__builtin__'] = __builtin__
711 ss = StringSubber(env, mode, target, source, conv, gvars)
712 result = ss.substitute(strSubst, dict)
715 del gvars['__builtin__']
719 if is_String(result):
720 # Remove $(-$) pairs and any stuff in between,
721 # if that's appropriate.
722 remove = _regex_remove[mode]
724 result = remove.sub('', result)
725 if mode != SUBST_RAW:
726 # Compress strings of white space characters into
728 result = string.strip(_space_sep.sub(' ', result))
732 def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=None, conv=None, gvars=None):
733 """Substitute construction variables in a string (or list or other
734 object) and separate the arguments into a command list.
736 The companion scons_subst() function (above) handles basic
737 substitutions within strings, so see that function instead
738 if that's what you're looking for.
740 class ListSubber(UserList.UserList):
741 """A class to construct the results of a scons_subst_list() call.
743 Like StringSubber, this class binds a specific construction
744 environment, mode, target and source with two methods
745 (substitute() and expand()) that handle the expansion.
747 In addition, however, this class is used to track the state of
748 the result(s) we're gathering so we can do the appropriate thing
749 whenever we have to append another word to the result--start a new
750 line, start a new word, append to the current word, etc. We do
751 this by setting the "append" attribute to the right method so
752 that our wrapper methods only need ever call ListSubber.append(),
753 and the rest of the object takes care of doing the right thing
756 def __init__(self, env, mode, target, source, conv, gvars):
757 UserList.UserList.__init__(self, [])
765 if self.mode == SUBST_RAW:
766 self.add_strip = lambda x, s=self: s.append(x)
768 self.add_strip = lambda x, s=self: None
772 def expand(self, s, lvars, within_list):
773 """Expand a single "token" as necessary, appending the
774 expansion to the current result.
776 This handles expanding different types of things (strings,
777 lists, callables) appropriately. It calls the wrapper
778 substitute() method to re-expand things as necessary, so that
779 the results of expansions of side-by-side strings still get
780 re-evaluated separately, not smushed together.
786 except (IndexError, ValueError):
793 self.open_strip('$(')
795 self.close_strip('$)')
801 s = eval(key, self.gvars, lvars)
802 except (IndexError, NameError, TypeError):
804 except SyntaxError,e:
806 raise SCons.Errors.BuildError, (self.target[0], "Syntax error `%s' trying to evaluate `%s'" % (e,s))
808 raise SCons.Errors.UserError, "Syntax error `%s' trying to evaluate `%s'" % (e,s)
810 # Before re-expanding the result, handle
811 # recursive expansion by copying the local
812 # variable dictionary and overwriting a null
813 # string for the value of the variable name
816 var = string.split(key, '.')[0]
818 self.substitute(s, lv, 0)
824 self.substitute(a, lvars, 1)
828 s = s(target=self.target,
831 for_signature=(self.mode != SUBST_CMD))
833 # This probably indicates that it's a callable
834 # object that doesn't match our calling arguments
837 self.substitute(s, lvars, within_list)
843 def substitute(self, args, lvars, within_list):
844 """Substitute expansions in an argument or list of arguments.
846 This serves as a wrapper for splitting up a string into
850 if is_String(args) and not isinstance(args, CmdStringHolder):
851 args = _separate_args.findall(args)
853 if a[0] in ' \t\n\r\f\v':
861 self.expand(a, lvars, within_list)
863 self.expand(args, lvars, within_list)
866 """Arrange for the next word to start a new line. This
867 is like starting a new word, except that we have to append
868 another line to the result."""
869 UserList.UserList.append(self, [])
873 """Arrange for the next word to append to the end of the
874 current last word in the result."""
875 self.append = self.add_to_current_word
878 """Arrange for the next word to start a new word."""
879 self.append = self.add_new_word
881 def add_to_current_word(self, x):
882 """Append the string x to the end of the current last word
883 in the result. If that is not possible, then just add
884 it as a new word. Make sure the entire concatenated string
885 inherits the object attributes of x (in particular, the
886 escape function) by wrapping it as CmdStringHolder."""
888 if not self.in_strip or self.mode != SUBST_SIG:
890 current_word = self[-1][-1]
894 # All right, this is a hack and it should probably
895 # be refactored out of existence in the future.
896 # The issue is that we want to smoosh words together
897 # and make one file name that gets escaped if
898 # we're expanding something like foo$EXTENSION,
899 # but we don't want to smoosh them together if
900 # it's something like >$TARGET, because then we'll
901 # treat the '>' like it's part of the file name.
902 # So for now, just hard-code looking for the special
903 # command-line redirection characters...
905 last_char = str(current_word)[-1]
908 if last_char in '<>|':
912 literal1 = self.literal(self[-1][-1])
913 literal2 = self.literal(x)
916 y = CmdStringHolder(y, literal1 or literal2)
919 def add_new_word(self, x):
920 if not self.in_strip or self.mode != SUBST_SIG:
921 literal = self.literal(x)
924 x = CmdStringHolder(x, literal)
926 self.append = self.add_to_current_word
928 def literal(self, x):
931 except AttributeError:
936 def open_strip(self, x):
937 """Handle the "open strip" $( token."""
941 def close_strip(self, x):
942 """Handle the "close strip" $) token."""
947 dict = subst_dict(target, source)
949 conv = _strconv[mode]
951 gvars = env.Dictionary()
953 # We're (most likely) going to eval() things. If Python doesn't
954 # find a __builtin__ value in the global dictionary used for eval(),
955 # it copies the current __builtin__ values for you. Avoid this by
956 # setting it explicitly and then deleting, so we don't pollute the
957 # construction environment Dictionary(ies) that are typically used
959 gvars['__builtins__'] = __builtins__
961 ls = ListSubber(env, mode, target, source, conv, gvars)
962 ls.substitute(strSubst, dict, 0)
965 del gvars['__builtins__']
971 def scons_subst_once(strSubst, env, key):
972 """Perform single (non-recursive) substitution of a single
973 construction variable keyword.
975 This is used when setting a variable when copying or overriding values
976 in an Environment. We want to capture (expand) the old value before
977 we override it, so people can do things like:
979 env2 = env.Copy(CCFLAGS = '$CCFLAGS -g')
981 We do this with some straightforward, brute-force code here...
983 if type(strSubst) == types.StringType and string.find(strSubst, '$') < 0:
986 matchlist = ['$' + key, '${' + key + '}']
987 val = env.get(key, '')
988 def sub_match(match, val=val, matchlist=matchlist):
993 return string.join(map(str, a))
997 if is_List(strSubst):
1001 if arg in matchlist:
1008 result.append(_dollar_exps.sub(sub_match, arg))
1012 elif is_String(strSubst):
1013 return _dollar_exps.sub(sub_match, strSubst)
1017 def render_tree(root, child_func, prune=0, margin=[0], visited={}):
1019 Render a tree of nodes into an ASCII tree view.
1020 root - the root node of the tree
1021 child_func - the function called to get the children of a node
1022 prune - don't visit the same node twice
1023 margin - the format of the left margin to use for children of root.
1024 1 results in a pipe, and 0 results in no pipe.
1025 visited - a dictionary of visited nodes in the current branch if not prune,
1026 or in the whole tree if prune.
1031 if visited.has_key(rname):
1034 children = child_func(root)
1036 for pipe in margin[:-1]:
1038 retval = retval + "| "
1040 retval = retval + " "
1042 retval = retval + "+-" + rname + "\n"
1044 visited = copy.copy(visited)
1047 for i in range(len(children)):
1048 margin.append(i<len(children)-1)
1049 retval = retval + render_tree(children[i], child_func, prune, margin, visited
1055 def print_tree(root, child_func, prune=0, margin=[0], visited={}):
1057 Print a tree of nodes. This is like render_tree, except it prints
1058 lines directly instead of creating a string representation in memory,
1059 so that huge trees can be printed.
1061 root - the root node of the tree
1062 child_func - the function called to get the children of a node
1063 prune - don't visit the same node twice
1064 margin - the format of the left margin to use for children of root.
1065 1 results in a pipe, and 0 results in no pipe.
1066 visited - a dictionary of visited nodes in the current branch if not prune,
1067 or in the whole tree if prune.
1072 if visited.has_key(rname):
1076 return [" ","| "][m]
1077 print string.join(map(MMM, margin[:-1]), '') + "+-" + rname
1082 children = child_func(root)
1085 map(lambda C, cf=child_func, p=prune, m=margin, v=visited:
1086 print_tree(C, cf, p, m, v),
1089 print_tree(children[-1], child_func, prune, margin, visited)
1094 return type(e) is types.DictType or isinstance(e, UserDict)
1097 return type(e) is types.ListType or isinstance(e, UserList.UserList)
1099 if hasattr(types, 'UnicodeType'):
1101 return type(e) is types.StringType \
1102 or type(e) is types.UnicodeType \
1103 or isinstance(e, UserString)
1106 return type(e) is types.StringType or isinstance(e, UserString)
1109 return is_String(e) or not is_List(e)
1111 def flatten(sequence, scalarp=is_Scalar, result=None):
1114 for item in sequence:
1118 flatten(item, scalarp, result)
1122 """A simple generic Proxy class, forwarding all calls to
1123 subject. So, for the benefit of the python newbie, what does
1124 this really mean? Well, it means that you can take an object, let's
1125 call it 'objA', and wrap it in this Proxy class, with a statement
1128 proxyObj = Proxy(objA),
1130 Then, if in the future, you do something like this
1134 since Proxy does not have a 'var1' attribute (but presumably objA does),
1135 the request actually is equivalent to saying
1139 Inherit from this class to create a Proxy."""
1141 def __init__(self, subject):
1142 """Wrap an object as a Proxy object"""
1143 self.__subject = subject
1145 def __getattr__(self, name):
1146 """Retrieve an attribute from the wrapped object. If the named
1147 attribute doesn't exist, AttributeError is raised"""
1148 return getattr(self.__subject, name)
1151 """Retrieve the entire wrapped object"""
1152 return self.__subject
1154 def __cmp__(self, other):
1155 if issubclass(other.__class__, self.__subject.__class__):
1156 return cmp(self.__subject, other)
1157 return cmp(self.__dict__, other.__dict__)
1159 # attempt to load the windows registry module:
1167 RegOpenKeyEx = _winreg.OpenKeyEx
1168 RegEnumKey = _winreg.EnumKey
1169 RegEnumValue = _winreg.EnumValue
1170 RegQueryValueEx = _winreg.QueryValueEx
1171 RegError = _winreg.error
1180 RegOpenKeyEx = win32api.RegOpenKeyEx
1181 RegEnumKey = win32api.RegEnumKey
1182 RegEnumValue = win32api.RegEnumValue
1183 RegQueryValueEx = win32api.RegQueryValueEx
1184 RegError = win32api.error
1187 class _NoError(Exception):
1192 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
1193 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
1194 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
1195 HKEY_USERS = hkey_mod.HKEY_USERS
1197 def RegGetValue(root, key):
1198 """This utility function returns a value in the registry
1199 without having to open the key first. Only available on
1200 Windows platforms with a version of Python that can read the
1201 registry. Returns the same thing as
1202 SCons.Util.RegQueryValueEx, except you just specify the entire
1203 path to the value, and don't have to bother opening the key
1207 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
1208 r'SOFTWARE\Microsoft\Windows\CurrentVersion')
1209 out = SCons.Util.RegQueryValueEx(k,
1213 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1214 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
1216 # I would use os.path.split here, but it's not a filesystem
1218 p = key.rfind('\\') + 1
1221 k = SCons.Util.RegOpenKeyEx(root, keyp)
1222 return SCons.Util.RegQueryValueEx(k,val)
1224 if sys.platform == 'win32':
1226 def WhereIs(file, path=None, pathext=None, reject=[]):
1229 path = os.environ['PATH']
1233 path = string.split(path, os.pathsep)
1236 pathext = os.environ['PATHEXT']
1238 pathext = '.COM;.EXE;.BAT;.CMD'
1239 if is_String(pathext):
1240 pathext = string.split(pathext, os.pathsep)
1242 if string.lower(ext) == string.lower(file[-len(ext):]):
1245 if not is_List(reject):
1248 f = os.path.join(dir, file)
1251 if os.path.isfile(fext):
1255 return os.path.normpath(fext)
1259 elif os.name == 'os2':
1261 def WhereIs(file, path=None, pathext=None, reject=[]):
1264 path = os.environ['PATH']
1268 path = string.split(path, os.pathsep)
1270 pathext = ['.exe', '.cmd']
1272 if string.lower(ext) == string.lower(file[-len(ext):]):
1275 if not is_List(reject):
1278 f = os.path.join(dir, file)
1281 if os.path.isfile(fext):
1285 return os.path.normpath(fext)
1291 def WhereIs(file, path=None, pathext=None, reject=[]):
1294 path = os.environ['PATH']
1298 path = string.split(path, os.pathsep)
1299 if not is_List(reject):
1302 f = os.path.join(d, file)
1303 if os.path.isfile(f):
1308 if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
1312 return os.path.normpath(f)
1316 def PrependPath(oldpath, newpath, sep = os.pathsep):
1317 """This prepends newpath elements to the given oldpath. Will only
1318 add any particular path once (leaving the first one it encounters
1319 and ignoring the rest, to preserve path order), and will
1320 os.path.normpath and os.path.normcase all paths to help assure
1321 this. This can also handle the case where the given old path
1322 variable is a list instead of a string, in which case a list will
1323 be returned instead of a string.
1326 Old Path: "/foo/bar:/foo"
1327 New Path: "/biz/boom:/foo"
1328 Result: "/biz/boom:/foo:/foo/bar"
1334 if not is_List(orig):
1335 paths = string.split(paths, sep)
1338 if is_List(newpath):
1341 newpaths = string.split(newpath, sep)
1343 newpaths = newpaths + paths # prepend new paths
1347 # now we add them only if they are unique
1348 for path in newpaths:
1349 normpath = os.path.normpath(os.path.normcase(path))
1350 if path and not normpath in normpaths:
1352 normpaths.append(normpath)
1357 return string.join(paths, sep)
1359 def AppendPath(oldpath, newpath, sep = os.pathsep):
1360 """This appends new path elements to the given old path. Will
1361 only add any particular path once (leaving the last one it
1362 encounters and ignoring the rest, to preserve path order), and
1363 will os.path.normpath and os.path.normcase all paths to help
1364 assure this. This can also handle the case where the given old
1365 path variable is a list instead of a string, in which case a list
1366 will be returned instead of a string.
1369 Old Path: "/foo/bar:/foo"
1370 New Path: "/biz/boom:/foo"
1371 Result: "/foo/bar:/biz/boom:/foo"
1377 if not is_List(orig):
1378 paths = string.split(paths, sep)
1381 if is_List(newpath):
1384 newpaths = string.split(newpath, sep)
1386 newpaths = paths + newpaths # append new paths
1391 # now we add them only of they are unique
1392 for path in newpaths:
1393 normpath = os.path.normpath(os.path.normcase(path))
1394 if path and not normpath in normpaths:
1396 normpaths.append(normpath)
1403 return string.join(paths, sep)
1406 def dir_index(directory):
1408 for f in os.listdir(directory):
1409 fullname = os.path.join(directory, f)
1410 files.append(fullname)
1412 # os.listdir() isn't guaranteed to return files in any specific order,
1413 # but some of the test code expects sorted output.
1417 def fs_delete(path, remove=1):
1419 if os.path.exists(path):
1420 if os.path.isfile(path):
1421 if remove: os.unlink(path)
1422 display("Removed " + path)
1423 elif os.path.isdir(path) and not os.path.islink(path):
1424 # delete everything in the dir
1425 for p in dir_index(path):
1426 if os.path.isfile(p):
1427 if remove: os.unlink(p)
1428 display("Removed " + p)
1430 fs_delete(p, remove)
1431 # then delete dir itself
1432 if remove: os.rmdir(path)
1433 display("Removed directory " + path)
1435 print "scons: Could not remove '%s':" % str(path), e.strerror
1437 if sys.platform == 'cygwin':
1438 def get_native_path(path):
1439 """Transforms an absolute path into a native path for the system. In
1440 Cygwin, this converts from a Cygwin path to a Win32 one."""
1441 return string.replace(os.popen('cygpath -w ' + path).read(), '\n', '')
1443 def get_native_path(path):
1444 """Transforms an absolute path into a native path for the system.
1445 Non-Cygwin version, just leave the path alone."""
1448 display = DisplayEngine()
1453 elif is_String(arg):
1454 return string.split(arg)
1458 class CLVar(UserList.UserList):
1459 """A class for command-line construction variables.
1461 This is a list that uses Split() to split an initial string along
1462 white-space arguments, and similarly to split any strings that get
1463 added. This allows us to Do the Right Thing with Append() and
1464 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
1465 arg2') regardless of whether a user adds a list or a string to a
1466 command-line construction variable.
1468 def __init__(self, seq = []):
1469 UserList.UserList.__init__(self, Split(seq))
1470 def __coerce__(self, other):
1471 return (self, CLVar(other))
1473 return string.join(self.data)
1475 # A dictionary that preserves the order in which items are added.
1476 # Submitted by David Benjamin to ActiveState's Python Cookbook web site:
1477 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
1478 # Including fixes/enhancements from the follow-on discussions.
1479 class OrderedDict(UserDict):
1480 def __init__(self, dict = None):
1482 UserDict.__init__(self, dict)
1484 def __delitem__(self, key):
1485 UserDict.__delitem__(self, key)
1486 self._keys.remove(key)
1488 def __setitem__(self, key, item):
1489 UserDict.__setitem__(self, key, item)
1490 if key not in self._keys: self._keys.append(key)
1493 UserDict.clear(self)
1497 dict = OrderedDict()
1502 return zip(self._keys, self.values())
1505 return self._keys[:]
1509 key = self._keys[-1]
1511 raise KeyError('dictionary is empty')
1518 def setdefault(self, key, failobj = None):
1519 UserDict.setdefault(self, key, failobj)
1520 if key not in self._keys: self._keys.append(key)
1522 def update(self, dict):
1523 for (key, val) in dict.items():
1524 self.__setitem__(key, val)
1527 return map(self.get, self._keys)
1529 class Selector(OrderedDict):
1530 """A callable ordered dictionary that maps file suffixes to
1531 dictionary values. We preserve the order in which items are added
1532 so that get_suffix() calls always return the first suffix added."""
1533 def __call__(self, env, source):
1535 ext = splitext(str(source[0]))[1]
1541 # Try to perform Environment substitution on the keys of
1542 # the dictionary before giving up.
1544 for (k,v) in self.items():
1547 if s_dict.has_key(s_k):
1548 # We only raise an error when variables point
1549 # to the same suffix. If one suffix is literal
1550 # and a variable suffix contains this literal,
1551 # the literal wins and we don't raise an error.
1552 raise KeyError, (s_dict[s_k][0], k, s_k)
1555 return s_dict[ext][1]
1563 if sys.platform == 'cygwin':
1564 # On Cygwin, os.path.normcase() lies, so just report back the
1565 # fact that the underlying Win32 OS is case-insensitive.
1566 def case_sensitive_suffixes(s1, s2):
1569 def case_sensitive_suffixes(s1, s2):
1570 return (os.path.normcase(s1) != os.path.normcase(s2))
1572 def adjustixes(fname, pre, suf):
1574 path, fn = os.path.split(os.path.normpath(fname))
1575 if fn[:len(pre)] != pre:
1576 fname = os.path.join(path, pre + fn)
1577 # Only append a suffix if the file does not have one.
1578 if suf and not splitext(fname)[1] and fname[-len(suf):] != suf:
1584 """Return a list of the elements in s, but without duplicates.
1586 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
1587 unique("abcabc") some permutation of ["a", "b", "c"], and
1588 unique(([1, 2], [2, 3], [1, 2])) some permutation of
1591 For best speed, all sequence elements should be hashable. Then
1592 unique() will usually work in linear time.
1594 If not possible, the sequence elements should enjoy a total
1595 ordering, and if list(s).sort() doesn't raise TypeError it's
1596 assumed that they do enjoy a total ordering. Then unique() will
1597 usually work in O(N*log2(N)) time.
1599 If that's not possible either, the sequence elements must support
1600 equality-testing. Then unique() will usually work in quadratic
1608 # Try using a dict first, as that's the fastest and will usually
1609 # work. If it doesn't work, it will usually fail quickly, so it
1610 # usually doesn't cost much to *try* it. It requires that all the
1611 # sequence elements be hashable, and support equality comparison.
1617 del u # move on to the next method
1621 # We can't hash all the elements. Second fastest is to sort,
1622 # which brings the equal elements together; then duplicates are
1623 # easy to weed out in a single pass.
1624 # NOTE: Python's list.sort() was designed to be efficient in the
1625 # presence of many duplicate elements. This isn't true of all
1626 # sort functions in all languages or libraries, so this approach
1627 # is more effective in Python than it may be elsewhere.
1632 del t # move on to the next method
1639 t[lasti] = last = t[i]
1644 # Brute force is all that's left.
1651 # Much of the logic here was originally based on recipe 4.9 from the
1652 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
1655 def __init__(self, fileobj):
1656 self.fileobj = fileobj
1661 line = self.fileobj.readline()
1664 if line[-2:] == '\\\n':
1665 result.append(line[:-2])
1669 return string.join(result, '')
1671 def readlines(self):
1674 line = self.readline()