3 Base class for construction Environments. These are
4 the primary objects used to communicate dependency and
5 construction information to the build engine.
7 Keyword arguments supplied when the construction Environment
8 is created are construction variables used to initialize the
15 # Permission is hereby granted, free of charge, to any person obtaining
16 # a copy of this software and associated documentation files (the
17 # "Software"), to deal in the Software without restriction, including
18 # without limitation the rights to use, copy, modify, merge, publish,
19 # distribute, sublicense, and/or sell copies of the Software, and to
20 # permit persons to whom the Software is furnished to do so, subject to
21 # the following conditions:
23 # The above copyright notice and this permission notice shall be included
24 # in all copies or substantial portions of the Software.
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
27 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
42 from UserDict import UserDict
46 from SCons.Debug import logInstanceCreation
50 import SCons.Node.Alias
52 import SCons.Node.Python
57 import SCons.Sig.TimeStamp
70 # Pull UserError into the global name space for the benefit of
71 # Environment().SourceSignatures(), which has some import statements
72 # which seem to mess up its ability to reference SCons directly.
73 UserError = SCons.Errors.UserError
75 def installFunc(target, source, env):
76 """Install a source file into a target using the function specified
77 as the INSTALL construction variable."""
79 install = env['INSTALL']
81 raise SCons.Errors.UserError('Missing INSTALL construction variable.')
82 return install(target[0].path, source[0].path, env)
84 def installString(target, source, env):
85 return 'Install file: "%s" as "%s"' % (source[0], target[0])
87 installAction = SCons.Action.Action(installFunc, installString)
89 InstallBuilder = SCons.Builder.Builder(action=installAction,
90 name='InstallBuilder')
92 def alias_builder(env, target, source):
95 AliasBuilder = SCons.Builder.Builder(action = alias_builder,
96 target_factory = SCons.Node.Alias.default_ans.Alias,
97 source_factory = SCons.Node.FS.default_fs.Entry,
103 """deepcopy lists and dictionaries, and just copy the reference
104 for everything else."""
105 if SCons.Util.is_Dict(x):
108 copy[key] = our_deepcopy(x[key])
109 elif SCons.Util.is_List(x):
110 copy = map(our_deepcopy, x)
112 copy = x.__class__(copy)
113 except AttributeError:
119 def apply_tools(env, tools, toolpath):
122 # Filter out null tools from the list.
123 for tool in filter(None, tools):
124 if SCons.Util.is_List(tool) or type(tool)==type(()):
126 toolargs = tool[1] # should be a dict of kw args
127 tool = apply(env.Tool, (toolname, toolpath), toolargs)
129 env.Tool(tool, toolpath)
131 # These names are controlled by SCons; users should never set or override
132 # them. This warning can optionally be turned off, but scons will still
133 # ignore the illegal variable names even if it's off.
134 reserved_construction_var_names = \
135 ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
137 def copy_non_reserved_keywords(dict):
138 result = our_deepcopy(dict)
139 for k in result.keys():
140 if k in reserved_construction_var_names:
141 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
142 "Ignoring attempt to set reserved variable `%s'" % k)
146 class BuilderWrapper:
147 """Wrapper class that associates an environment with a Builder at
149 def __init__(self, env, builder):
151 self.builder = builder
153 def __call__(self, target=None, source=_null, *args, **kw):
157 if not target is None and not SCons.Util.is_List(target):
159 if not source is None and not SCons.Util.is_List(source):
161 return apply(self.builder, (self.env, target, source) + args, kw)
163 # This allows a Builder to be executed directly
164 # through the Environment to which it's attached.
165 # In practice, we shouldn't need this, because
166 # builders actually get executed through a Node.
167 # But we do have a unit test for this, and can't
168 # yet rule out that it would be useful in the
169 # future, so leave it for now.
170 def execute(self, **kw):
172 apply(self.builder.execute, (), kw)
174 class BuilderDict(UserDict):
175 """This is a dictionary-like class used by an Environment to hold
176 the Builders. We need to do this because every time someone changes
177 the Builders in the Environment's BUILDERS dictionary, we must
178 update the Environment's attributes."""
179 def __init__(self, dict, env):
180 # Set self.env before calling the superclass initialization,
181 # because it will end up calling our other methods, which will
182 # need to point the values in this dictionary to self.env.
184 UserDict.__init__(self, dict)
186 def __setitem__(self, item, val):
187 UserDict.__setitem__(self, item, val)
189 self.setenvattr(item, val)
190 except AttributeError:
191 # Have to catch this because sometimes __setitem__ gets
192 # called out of __init__, when we don't have an env
193 # attribute yet, nor do we want one!
196 def setenvattr(self, item, val):
197 """Set the corresponding environment attribute for this Builder.
199 If the value is already a BuilderWrapper, we pull the builder
200 out of it and make another one, so that making a copy of an
201 existing BuilderDict is guaranteed separate wrappers for each
202 Builder + Environment pair."""
204 builder = val.builder
205 except AttributeError:
207 setattr(self.env, item, BuilderWrapper(self.env, builder))
209 def __delitem__(self, item):
210 UserDict.__delitem__(self, item)
211 delattr(self.env, item)
213 def update(self, dict):
214 for i, v in dict.items():
215 self.__setitem__(i, v)
217 class SubstitutionEnvironment:
218 """Base class for different flavors of construction environments.
220 This class contains a minimal set of methods that handle contruction
221 variable expansion and conversion of strings to Nodes, which may or
222 may not be actually useful as a stand-alone class. Which methods
223 ended up in this class is pretty arbitrary right now. They're
224 basically the ones which we've empirically determined are common to
225 the different construction environment subclasses, and most of the
226 others that use or touch the underlying dictionary of construction
229 Eventually, this class should contain all the methods that we
230 determine are necessary for a "minimal" interface to the build engine.
231 A full "native Python" SCons environment has gotten pretty heavyweight
232 with all of the methods and Tools and construction variables we've
233 jammed in there, so it would be nice to have a lighter weight
234 alternative for interfaces that don't need all of the bells and
235 whistles. (At some point, we'll also probably rename this class
236 "Base," since that more reflects what we want this class to become,
237 but because we've released comments that tell people to subclass
238 Environment.Base to create their own flavors of construction
239 environment, we'll save that for a future refactoring when this
240 class actually becomes useful.)
243 __metaclass__ = SCons.Memoize.Memoized_Metaclass
245 def __init__(self, **kw):
246 """Initialization of an underlying SubstitutionEnvironment class.
248 if __debug__: logInstanceCreation(self)
249 self.fs = SCons.Node.FS.default_fs
250 self.ans = SCons.Node.Alias.default_ans
251 self.lookup_list = SCons.Node.arg2nodes_lookups
252 self._dict = kw.copy()
254 def __cmp__(self, other):
255 return cmp(self._dict, other._dict)
257 def __delitem__(self, key):
261 def __getitem__(self, key):
262 return self._dict[key]
264 def __setitem__(self, key, value):
266 if key in reserved_construction_var_names:
267 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
268 "Ignoring attempt to set reserved variable `%s'" % key)
269 elif key == 'BUILDERS':
275 self._dict[key] = BuilderDict(kwbd, self)
276 self._dict[key].update(value)
277 elif key == 'SCANNERS':
278 self._dict[key] = value
279 self.scanner_map_delete()
281 if not SCons.Util.is_valid_construction_var(key):
282 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
283 self._dict[key] = value
285 def get(self, key, default=None):
286 "Emulates the get() method of dictionaries."""
287 return self._dict.get(key, default)
289 def has_key(self, key):
290 return self._dict.has_key(key)
293 return self._dict.items()
295 def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
296 if node_factory is _null:
297 node_factory = self.fs.File
298 if lookup_list is _null:
299 lookup_list = self.lookup_list
304 if SCons.Util.is_List(args):
305 args = SCons.Util.flatten(args)
311 if SCons.Util.is_String(v):
313 for l in lookup_list:
318 if SCons.Util.is_String(n):
319 n = self.subst(n, raw=1)
322 if SCons.Util.is_List(n):
327 v = node_factory(self.subst(v, raw=1))
328 if SCons.Util.is_List(v):
343 def subst(self, string, raw=0, target=None, source=None, conv=None):
344 """Recursively interpolates construction variables from the
345 Environment into the specified string, returning the expanded
346 result. Construction variables are specified by a $ prefix
347 in the string and begin with an initial underscore or
348 alphabetic character followed by any number of underscores
349 or alphanumeric characters. The construction variable names
350 may be surrounded by curly braces to separate the name from
355 lvars['__env__'] = self
356 return SCons.Util.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
358 def subst_kw(self, kw, raw=0, target=None, source=None):
360 for k, v in kw.items():
361 k = self.subst(k, raw, target, source)
362 if SCons.Util.is_String(v):
363 v = self.subst(v, raw, target, source)
367 def subst_list(self, string, raw=0, target=None, source=None, conv=None):
368 """Calls through to SCons.Util.scons_subst_list(). See
369 the documentation for that function."""
372 lvars['__env__'] = self
373 return SCons.Util.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
375 def subst_path(self, path, target=None, source=None):
376 """Substitute a path list, turning EntryProxies into Nodes
377 and leaving Nodes (and other objects) as-is."""
379 if not SCons.Util.is_List(path):
383 """This is the "string conversion" routine that we have our
384 substitutions use to return Nodes, not strings. This relies
385 on the fact that an EntryProxy object has a get() method that
386 returns the underlying Node that it wraps, which is a bit of
387 architectural dependence that we might need to break or modify
388 in the future in response to additional requirements."""
391 except AttributeError:
399 if SCons.Util.is_String(p):
400 p = self.subst(p, target=target, source=source, conv=s)
401 if SCons.Util.is_List(p):
405 # We have an object plus a string, or multiple
406 # objects that we need to smush together. No choice
407 # but to make them into a string.
408 p = string.join(map(SCons.Util.to_String, p), '')
414 subst_target_source = subst
416 def Override(self, overrides):
418 Produce a modified environment whose variables are overriden by
419 the overrides dictionaries. "overrides" is a dictionary that
420 will override the variables of this environment.
422 This function is much more efficient than Copy() or creating
423 a new Environment because it doesn't copy the construction
424 environment dictionary, it just wraps the underlying construction
425 environment, and doesn't even create a wrapper object if there
429 o = copy_non_reserved_keywords(overrides)
431 for key, value in o.items():
432 overrides[key] = SCons.Util.scons_subst_once(value, self, key)
434 env = OverrideEnvironment(self, overrides)
439 class Base(SubstitutionEnvironment):
440 """Base class for "real" construction Environments. These are the
441 primary objects used to communicate dependency and construction
442 information to the build engine.
444 Keyword arguments supplied when the construction Environment
445 is created are construction variables used to initialize the
449 __metaclass__ = SCons.Memoize.Memoized_Metaclass
451 #######################################################################
452 # This is THE class for interacting with the SCons build engine,
453 # and it contains a lot of stuff, so we're going to try to keep this
454 # a little organized by grouping the methods.
455 #######################################################################
457 #######################################################################
458 # Methods that make an Environment act like a dictionary. These have
459 # the expected standard names for Python mapping objects. Note that
460 # we don't actually make an Environment a subclass of UserDict for
461 # performance reasons. Note also that we only supply methods for
462 # dictionary functionality that we actually need and use.
463 #######################################################################
472 Initialization of a basic SCons construction environment,
473 including setting up special construction variables like BUILDER,
474 PLATFORM, etc., and searching for and applying available Tools.
476 Note that we do *not* call the underlying base class
477 (SubsitutionEnvironment) initialization, because we need to
478 initialize things in a very specific order that doesn't work
479 with the much simpler base class initialization.
481 if __debug__: logInstanceCreation(self)
482 self.fs = SCons.Node.FS.default_fs
483 self.ans = SCons.Node.Alias.default_ans
484 self.lookup_list = SCons.Node.arg2nodes_lookups
485 self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
487 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
490 platform = self._dict.get('PLATFORM', None)
492 platform = SCons.Platform.Platform()
493 if SCons.Util.is_String(platform):
494 platform = SCons.Platform.Platform(platform)
495 self._dict['PLATFORM'] = str(platform)
498 # Apply the passed-in variables before calling the tools,
499 # because they may use some of them:
500 apply(self.Replace, (), kw)
502 # Update the environment with the customizable options
503 # before calling the tools, since they may use some of the options:
508 tools = self._dict.get('TOOLS', None)
511 apply_tools(self, tools, toolpath)
513 # Reapply the passed in variables after calling the tools,
514 # since they should overide anything set by the tools:
515 apply(self.Replace, (), kw)
517 # Update the environment with the customizable options
518 # after calling the tools, since they should override anything
523 #######################################################################
524 # Utility methods that are primarily for internal use by SCons.
525 # These begin with lower-case letters.
526 #######################################################################
528 def get_calculator(self):
531 module = self._calc_module
532 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
533 except AttributeError:
534 # Note that we're calling get_calculator() here, so the
535 # DefaultEnvironment() must have a _calc_module attribute
536 # to avoid infinite recursion.
537 c = SCons.Defaults.DefaultEnvironment().get_calculator()
540 def get_builder(self, name):
541 """Fetch the builder with the specified name from the environment.
544 return self._dict['BUILDERS'][name]
551 scanners = self._dict['SCANNERS']
556 # Reverse the scanner list so that, if multiple scanners
557 # claim they can scan the same suffix, earlier scanners
558 # in the list will overwrite later scanners, so that
559 # the result looks like a "first match" to the user.
560 if not SCons.Util.is_List(scanners):
561 scanners = [scanners]
563 scanners = scanners[:] # copy so reverse() doesn't mod original
565 for scanner in scanners:
566 for k in scanner.get_skeys(self):
570 def get_scanner(self, skey):
571 """Find the appropriate scanner given a key (usually a file suffix).
583 def scanner_map_delete(self, kw=None):
584 """Delete the cached scanner map (if we need to).
586 if not kw is None and not kw.has_key('SCANNERS'):
590 def _update(self, dict):
591 """Update an environment's values directly, bypassing the normal
592 checks that occur when users try to set items.
595 self._dict.update(dict)
597 def use_build_signature(self):
599 return self._build_signature
600 except AttributeError:
601 b = SCons.Defaults.DefaultEnvironment()._build_signature
602 self._build_signature = b
605 #######################################################################
606 # Public methods for manipulating an Environment. These begin with
607 # upper-case letters. The essential characteristic of methods in
608 # this section is that they do *not* have corresponding same-named
609 # global functions. For example, a stand-alone Append() function
610 # makes no sense, because Append() is all about appending values to
611 # an Environment's construction variables.
612 #######################################################################
614 def Append(self, **kw):
615 """Append values to existing construction variables
618 kw = copy_non_reserved_keywords(kw)
619 for key, val in kw.items():
620 # It would be easier on the eyes to write this using
621 # "continue" statements whenever we finish processing an item,
622 # but Python 1.5.2 apparently doesn't let you use "continue"
623 # within try:-except: blocks, so we have to nest our code.
625 orig = self._dict[key]
627 # No existing variable in the environment, so just set
628 # it to the new value.
629 self._dict[key] = val
632 # Most straightforward: just try to add them
633 # together. This will work in most cases, when the
634 # original and new values are of compatible types.
635 self._dict[key] = orig + val
638 # Try to update a dictionary value with another.
639 # If orig isn't a dictionary, it won't have an
640 # update() method; if val isn't a dictionary,
641 # it won't have a keys() method. Either way,
642 # it's an AttributeError.
644 except AttributeError:
646 # Check if the original is a list.
647 add_to_orig = orig.append
648 except AttributeError:
649 # The original isn't a list, but the new
650 # value is (by process of elimination),
651 # so insert the original in the new value
652 # (if there's one to insert) and replace
653 # the variable with it.
656 self._dict[key] = val
658 # The original is a list, so append the new
659 # value to it (if there's a value to append).
662 self.scanner_map_delete(kw)
664 def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
665 """Append path elements to the path 'name' in the 'ENV'
666 dictionary for this environment. Will only add any particular
667 path once, and will normpath and normcase all paths to help
668 assure this. This can also handle the case where the env
669 variable is a list instead of a string.
673 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
674 orig = self._dict[envname][name]
676 nv = SCons.Util.AppendPath(orig, newpath, sep)
678 if not self._dict.has_key(envname):
679 self._dict[envname] = {}
681 self._dict[envname][name] = nv
683 def AppendUnique(self, **kw):
684 """Append values to existing construction variables
685 in an Environment, if they're not already there.
687 kw = copy_non_reserved_keywords(kw)
688 for key, val in kw.items():
689 if not self._dict.has_key(key) or not self._dict[key]:
690 self._dict[key] = val
691 elif SCons.Util.is_Dict(self._dict[key]) and \
692 SCons.Util.is_Dict(val):
693 self._dict[key].update(val)
694 elif SCons.Util.is_List(val):
696 if not SCons.Util.is_List(dk):
698 val = filter(lambda x, dk=dk: x not in dk, val)
699 self._dict[key] = dk + val
702 if SCons.Util.is_List(dk):
704 self._dict[key] = dk + val
706 self._dict[key] = self._dict[key] + val
707 self.scanner_map_delete(kw)
709 def Copy(self, tools=[], toolpath=[], **kw):
710 """Return a copy of a construction Environment. The
711 copy is like a Python "deep copy"--that is, independent
712 copies are made recursively of each objects--except that
713 a reference is copied when an object is not deep-copyable
714 (like a function). There are no references to any mutable
715 objects in the original Environment.
717 clone = copy.copy(self)
718 clone._dict = our_deepcopy(self._dict)
720 cbd = clone._dict['BUILDERS']
721 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
725 apply_tools(clone, tools, toolpath)
727 # Apply passed-in variables after the new tools.
728 kw = copy_non_reserved_keywords(kw)
730 for key, value in kw.items():
731 new[key] = SCons.Util.scons_subst_once(value, self, key)
732 apply(clone.Replace, (), new)
733 if __debug__: logInstanceCreation(self, 'EnvironmentCopy')
736 def Detect(self, progs):
737 """Return the first available program in progs. __cacheable__
739 if not SCons.Util.is_List(progs):
742 path = self.WhereIs(prog)
746 def Dictionary(self, *args):
749 dlist = map(lambda x, s=self: s._dict[x], args)
754 def Dump(self, key = None):
756 Using the standard Python pretty printer, dump the contents of the
757 scons build environment to stdout.
759 If the key passed in is anything other than None, then that will
760 be used as an index into the build environment dictionary and
761 whatever is found there will be fed into the pretty printer. Note
762 that this key is case sensitive.
765 pp = pprint.PrettyPrinter(indent=2)
767 dict = self.Dictionary(key)
769 dict = self.Dictionary()
770 return pp.pformat(dict)
772 def FindIxes(self, paths, prefix, suffix):
774 Search a list of paths for something that matches the prefix and suffix.
776 paths - the list of paths or nodes.
777 prefix - construction variable for the prefix.
778 suffix - construction variable for the suffix.
781 suffix = self.subst('$'+suffix)
782 prefix = self.subst('$'+prefix)
785 dir,name = os.path.split(str(path))
786 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
789 def ParseConfig(self, command, function=None, unique=1):
791 Use the specified function to parse the output of the command
792 in order to modify the current environment. The 'command' can
793 be a string or a list of strings representing a command and
794 it's arguments. 'Function' is an optional argument that takes
795 the environment and the output of the command. If no function is
796 specified, the output will be treated as the output of a typical
797 'X-config' command (i.e. gtk-config) and used to append to the
798 ASFLAGS, CCFLAGS, CPPFLAGS, CPPPATH, LIBPATH, LIBS, LINKFLAGS
799 and CCFLAGS variables.
802 # the default parse function
803 def parse_conf(env, output, fs=self.fs, unique=unique):
814 params = string.split(output)
815 append_next_arg_to='' # for multi-word args
817 if append_next_arg_to:
818 dict[append_next_arg_to].append(arg)
819 append_next_arg_to = ''
821 dict['LIBS'].append(fs.File(arg))
822 elif arg[:2] == '-L':
824 dict['LIBPATH'].append(arg[2:])
826 append_next_arg_to = 'LIBPATH'
827 elif arg[:2] == '-l':
829 dict['LIBS'].append(arg[2:])
831 append_next_arg_to = 'LIBS'
832 elif arg[:2] == '-I':
834 dict['CPPPATH'].append(arg[2:])
836 append_next_arg_to = 'CPPPATH'
837 elif arg[:4] == '-Wa,':
838 dict['ASFLAGS'].append(arg)
839 elif arg[:4] == '-Wl,':
840 dict['LINKFLAGS'].append(arg)
841 elif arg[:4] == '-Wp,':
842 dict['CPPFLAGS'].append(arg)
843 elif arg == '-framework':
844 dict['LINKFLAGS'].append(arg)
845 append_next_arg_to='LINKFLAGS'
846 elif arg == '-mno-cygwin':
847 dict['CCFLAGS'].append(arg)
848 dict['LINKFLAGS'].append(arg)
849 elif arg == '-mwindows':
850 dict['LINKFLAGS'].append(arg)
851 elif arg == '-pthread':
852 dict['CCFLAGS'].append(arg)
853 dict['LINKFLAGS'].append(arg)
855 dict['CCFLAGS'].append(arg)
857 appender = env.AppendUnique
859 appender = env.Append
860 apply(appender, (), dict)
863 function = parse_conf
864 if type(command) is type([]):
865 command = string.join(command)
866 command = self.subst(command)
867 return function(self, os.popen(command).read())
869 def ParseDepends(self, filename, must_exist=None, only_one=0):
871 Parse a mkdep-style file for explicit dependencies. This is
872 completely abusable, and should be unnecessary in the "normal"
873 case of proper SCons configuration, but it may help make
874 the transition from a Make hierarchy easier for some people
875 to swallow. It can also be genuinely useful when using a tool
876 that can write a .d file, but for which writing a scanner would
879 filename = self.subst(filename)
881 fp = open(filename, 'r')
886 lines = SCons.Util.LogicalLines(fp).readlines()
887 lines = filter(lambda l: l[0] != '#', lines)
891 target, depends = string.split(line, ':', 1)
892 except (AttributeError, TypeError, ValueError):
893 # Python 1.5.2 throws TypeError if line isn't a string,
894 # Python 2.x throws AttributeError because it tries
895 # to call line.split(). Either can throw ValueError
896 # if the line doesn't split into two or more elements.
899 tdlist.append((string.split(target), string.split(depends)))
901 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
903 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
904 for target, depends in tdlist:
905 self.Depends(target, depends)
907 def Platform(self, platform):
908 platform = self.subst(platform)
909 return SCons.Platform.Platform(platform)(self)
911 def Prepend(self, **kw):
912 """Prepend values to existing construction variables
915 kw = copy_non_reserved_keywords(kw)
916 for key, val in kw.items():
917 # It would be easier on the eyes to write this using
918 # "continue" statements whenever we finish processing an item,
919 # but Python 1.5.2 apparently doesn't let you use "continue"
920 # within try:-except: blocks, so we have to nest our code.
922 orig = self._dict[key]
924 # No existing variable in the environment, so just set
925 # it to the new value.
926 self._dict[key] = val
929 # Most straightforward: just try to add them
930 # together. This will work in most cases, when the
931 # original and new values are of compatible types.
932 self._dict[key] = val + orig
935 # Try to update a dictionary value with another.
936 # If orig isn't a dictionary, it won't have an
937 # update() method; if val isn't a dictionary,
938 # it won't have a keys() method. Either way,
939 # it's an AttributeError.
941 except AttributeError:
943 # Check if the added value is a list.
944 add_to_val = val.append
945 except AttributeError:
946 # The added value isn't a list, but the
947 # original is (by process of elimination),
948 # so insert the the new value in the original
949 # (if there's one to insert).
953 # The added value is a list, so append
954 # the original to it (if there's a value
958 self._dict[key] = val
959 self.scanner_map_delete(kw)
961 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
962 """Prepend path elements to the path 'name' in the 'ENV'
963 dictionary for this environment. Will only add any particular
964 path once, and will normpath and normcase all paths to help
965 assure this. This can also handle the case where the env
966 variable is a list instead of a string.
970 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
971 orig = self._dict[envname][name]
973 nv = SCons.Util.PrependPath(orig, newpath, sep)
975 if not self._dict.has_key(envname):
976 self._dict[envname] = {}
978 self._dict[envname][name] = nv
980 def PrependUnique(self, **kw):
981 """Append values to existing construction variables
982 in an Environment, if they're not already there.
984 kw = copy_non_reserved_keywords(kw)
985 for key, val in kw.items():
986 if not self._dict.has_key(key) or not self._dict[key]:
987 self._dict[key] = val
988 elif SCons.Util.is_Dict(self._dict[key]) and \
989 SCons.Util.is_Dict(val):
990 self._dict[key].update(val)
991 elif SCons.Util.is_List(val):
993 if not SCons.Util.is_List(dk):
995 val = filter(lambda x, dk=dk: x not in dk, val)
996 self._dict[key] = val + dk
999 if SCons.Util.is_List(dk):
1001 self._dict[key] = val + dk
1003 self._dict[key] = val + dk
1004 self.scanner_map_delete(kw)
1006 def Replace(self, **kw):
1007 """Replace existing construction variables in an Environment
1008 with new construction variables and/or values.
1011 kwbd = our_deepcopy(kw['BUILDERS'])
1013 self.__setitem__('BUILDERS', kwbd)
1016 kw = copy_non_reserved_keywords(kw)
1017 self._update(our_deepcopy(kw))
1018 self.scanner_map_delete(kw)
1020 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1022 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1024 env - Environment used to interpolate variables.
1025 path - the path that will be modified.
1026 old_prefix - construction variable for the old prefix.
1027 old_suffix - construction variable for the old suffix.
1028 new_prefix - construction variable for the new prefix.
1029 new_suffix - construction variable for the new suffix.
1031 old_prefix = self.subst('$'+old_prefix)
1032 old_suffix = self.subst('$'+old_suffix)
1034 new_prefix = self.subst('$'+new_prefix)
1035 new_suffix = self.subst('$'+new_suffix)
1037 dir,name = os.path.split(str(path))
1038 if name[:len(old_prefix)] == old_prefix:
1039 name = name[len(old_prefix):]
1040 if name[-len(old_suffix):] == old_suffix:
1041 name = name[:-len(old_suffix)]
1042 return os.path.join(dir, new_prefix+name+new_suffix)
1044 def SetDefault(self, **kw):
1046 if self._dict.has_key(k):
1048 apply(self.Replace, (), kw)
1050 def Tool(self, tool, toolpath=[], **kw):
1051 if SCons.Util.is_String(tool):
1052 tool = self.subst(tool)
1053 toolpath = map(self.subst, toolpath)
1054 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
1057 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1058 """Find prog in the path. __cacheable__
1062 path = self['ENV']['PATH']
1065 elif SCons.Util.is_String(path):
1066 path = self.subst(path)
1069 pathext = self['ENV']['PATHEXT']
1072 elif SCons.Util.is_String(pathext):
1073 pathext = self.subst(pathext)
1074 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1075 if path: return path
1078 #######################################################################
1079 # Public methods for doing real "SCons stuff" (manipulating
1080 # dependencies, setting attributes on targets, etc.). These begin
1081 # with upper-case letters. The essential characteristic of methods
1082 # in this section is that they all *should* have corresponding
1083 # same-named global functions.
1084 #######################################################################
1086 def Action(self, *args, **kw):
1087 nargs = self.subst(args)
1088 nkw = self.subst_kw(kw)
1089 return apply(SCons.Action.Action, nargs, nkw)
1091 def AddPreAction(self, files, action):
1092 nodes = self.arg2nodes(files, self.fs.Entry)
1093 action = SCons.Action.Action(action)
1095 n.add_pre_action(action)
1098 def AddPostAction(self, files, action):
1099 nodes = self.arg2nodes(files, self.fs.Entry)
1100 action = SCons.Action.Action(action)
1102 n.add_post_action(action)
1105 def Alias(self, target, source=[], action=None, **kw):
1106 tlist = self.arg2nodes(target, self.ans.Alias)
1107 if not SCons.Util.is_List(source):
1109 source = filter(None, source)
1113 # There are no source files and no action, so just
1114 # return a target list of classic Alias Nodes, without
1115 # any builder. The externally visible effect is that
1116 # this will make the wrapping Script.BuildTask class
1117 # say that there's "Nothing to be done" for this Alias,
1118 # instead of that it's "up to date."
1121 # No action, but there are sources. Re-call all the target
1122 # builders to add the sources to each target.
1125 bld = t.get_builder(AliasBuilder)
1126 result.extend(bld(self, t, source))
1129 nkw = self.subst_kw(kw)
1131 'action' : SCons.Action.Action(action),
1132 'source_factory' : self.fs.Entry,
1134 'is_explicit' : None,
1136 bld = apply(SCons.Builder.Builder, (), nkw)
1138 # Apply the Builder separately to each target so that the Aliases
1139 # stay separate. If we did one "normal" Builder call with the
1140 # whole target list, then all of the target Aliases would be
1141 # associated under a single Executor.
1144 # Calling the convert() method will cause a new Executor to be
1145 # created from scratch, so we have to explicitly initialize
1146 # it with the target's existing sources, plus our new ones,
1147 # so nothing gets lost.
1149 if b is None or b is AliasBuilder:
1152 nkw['action'] = b.action + action
1153 b = apply(SCons.Builder.Builder, (), nkw)
1155 result.extend(b(self, t, t.sources + source))
1158 def AlwaysBuild(self, *targets):
1161 tlist.extend(self.arg2nodes(t, self.fs.File))
1163 t.set_always_build()
1166 def BuildDir(self, build_dir, src_dir, duplicate=1):
1167 build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
1168 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1169 self.fs.BuildDir(build_dir, src_dir, duplicate)
1171 def Builder(self, **kw):
1172 nkw = self.subst_kw(kw)
1173 return apply(SCons.Builder.Builder, [], nkw)
1175 def CacheDir(self, path):
1176 self.fs.CacheDir(self.subst(path))
1178 def Clean(self, targets, files):
1180 tlist = self.arg2nodes(targets, self.fs.Entry)
1181 flist = self.arg2nodes(files, self.fs.Entry)
1184 CleanTargets[t].extend(flist)
1186 CleanTargets[t] = flist
1188 def Configure(self, *args, **kw):
1191 nargs = nargs + self.subst_list(args)[0]
1192 nkw = self.subst_kw(kw)
1193 nkw['_depth'] = kw.get('_depth', 0) + 1
1195 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1198 return apply(SCons.SConf.SConf, nargs, nkw)
1200 def Command(self, target, source, action, **kw):
1201 """Builds the supplied target files from the supplied
1202 source files using the supplied action. Action may
1203 be any type that the Builder constructor will accept
1207 'source_factory' : self.fs.Entry,
1209 try: bkw['source_scanner'] = kw['source_scanner']
1210 except KeyError: pass
1211 else: del kw['source_scanner']
1212 bld = apply(SCons.Builder.Builder, (), bkw)
1213 return apply(bld, (self, target, source), kw)
1215 def Depends(self, target, dependency):
1216 """Explicity specify that 'target's depend on 'dependency'."""
1217 tlist = self.arg2nodes(target, self.fs.Entry)
1218 dlist = self.arg2nodes(dependency, self.fs.Entry)
1220 t.add_dependency(dlist)
1223 def Dir(self, name, *args, **kw):
1226 return apply(self.fs.Dir, (self.subst(name),) + args, kw)
1228 def Environment(self, **kw):
1229 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1231 def Execute(self, action, *args, **kw):
1232 """Directly execute an action through an Environment
1234 action = apply(self.Action, (action,) + args, kw)
1235 return action([], [], self)
1237 def File(self, name, *args, **kw):
1240 return apply(self.fs.File, (self.subst(name),) + args, kw)
1242 def FindFile(self, file, dirs):
1243 file = self.subst(file)
1244 nodes = self.arg2nodes(dirs, self.fs.Dir)
1245 return SCons.Node.FS.find_file(file, nodes, self.fs.File)
1247 def Flatten(self, sequence):
1248 return SCons.Util.flatten(sequence)
1250 def GetBuildPath(self, files):
1251 result = map(str, self.arg2nodes(files, self.fs.Entry))
1252 if SCons.Util.is_List(files):
1257 def Ignore(self, target, dependency):
1258 """Ignore a dependency."""
1259 tlist = self.arg2nodes(target, self.fs.Entry)
1260 dlist = self.arg2nodes(dependency, self.fs.Entry)
1265 def Install(self, dir, source):
1266 """Install specified files in the given directory."""
1268 dnodes = self.arg2nodes(dir, self.fs.Dir)
1270 raise SCons.Errors.UserError, "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)
1272 sources = self.arg2nodes(source, self.fs.File)
1274 if SCons.Util.is_List(source):
1275 raise SCons.Errors.UserError, "Source `%s' of Install() contains one or more non-files. Install() source must be one or more files." % repr(map(str, source))
1277 raise SCons.Errors.UserError, "Source `%s' of Install() is not a file. Install() source must be one or more files." % str(source)
1279 for dnode in dnodes:
1281 target = self.fs.File(src.name, dnode)
1282 tgt.extend(InstallBuilder(self, target, src))
1285 def InstallAs(self, target, source):
1286 """Install sources as targets."""
1287 sources = self.arg2nodes(source, self.fs.File)
1288 targets = self.arg2nodes(target, self.fs.File)
1290 for src, tgt in map(lambda x, y: (x, y), sources, targets):
1291 result.extend(InstallBuilder(self, tgt, src))
1294 def Literal(self, string):
1295 return SCons.Util.Literal(string)
1297 def Local(self, *targets):
1299 for targ in targets:
1300 if isinstance(targ, SCons.Node.Node):
1304 for t in self.arg2nodes(targ, self.fs.Entry):
1309 def Precious(self, *targets):
1312 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1317 def Repository(self, *dirs, **kw):
1318 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1319 apply(self.fs.Repository, dirs, kw)
1321 def Scanner(self, *args, **kw):
1324 if SCons.Util.is_String(arg):
1325 arg = self.subst(arg)
1327 nkw = self.subst_kw(kw)
1328 return apply(SCons.Scanner.Scanner, nargs, nkw)
1330 def SConsignFile(self, name=".sconsign", dbm_module=None):
1331 name = self.subst(name)
1332 if not os.path.isabs(name):
1333 name = os.path.join(str(self.fs.SConstruct_dir), name)
1334 SCons.SConsign.File(name, dbm_module)
1336 def SideEffect(self, side_effect, target):
1337 """Tell scons that side_effects are built as side
1338 effects of building targets."""
1339 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1340 targets = self.arg2nodes(target, self.fs.Entry)
1342 for side_effect in side_effects:
1343 if side_effect.multiple_side_effect_has_builder():
1344 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1345 side_effect.add_source(targets)
1346 side_effect.side_effect = 1
1347 self.Precious(side_effect)
1348 for target in targets:
1349 target.side_effects.append(side_effect)
1352 def SourceCode(self, entry, builder):
1353 """Arrange for a source code builder for (part of) a tree."""
1354 entries = self.arg2nodes(entry, self.fs.Entry)
1355 for entry in entries:
1356 entry.set_src_builder(builder)
1359 def SourceSignatures(self, type):
1360 type = self.subst(type)
1362 import SCons.Sig.MD5
1363 self._calc_module = SCons.Sig.MD5
1364 elif type == 'timestamp':
1365 import SCons.Sig.TimeStamp
1366 self._calc_module = SCons.Sig.TimeStamp
1368 raise UserError, "Unknown source signature type '%s'"%type
1370 def Split(self, arg):
1371 """This function converts a string or list into a list of strings
1372 or Nodes. This makes things easier for users by allowing files to
1373 be specified as a white-space separated list to be split.
1374 The input rules are:
1375 - A single string containing names separated by spaces. These will be
1376 split apart at the spaces.
1377 - A single Node instance
1378 - A list containing either strings or Node instances. Any strings
1379 in the list are not split at spaces.
1380 In all cases, the function returns a list of Nodes and strings."""
1381 if SCons.Util.is_List(arg):
1382 return map(self.subst, arg)
1383 elif SCons.Util.is_String(arg):
1384 return string.split(self.subst(arg))
1386 return [self.subst(arg)]
1388 def TargetSignatures(self, type):
1389 type = self.subst(type)
1391 self._build_signature = 1
1392 elif type == 'content':
1393 self._build_signature = 0
1395 raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1397 def Value(self, value):
1400 return SCons.Node.Python.Value(value)
1402 class OverrideEnvironment(SubstitutionEnvironment):
1403 """A proxy that overrides variables in a wrapped construction
1404 environment by returning values from an overrides dictionary in
1405 preference to values from the underlying subject environment.
1407 This is a lightweight (I hope) proxy that passes through most use of
1408 attributes to the underlying Environment.Base class, but has just
1409 enough additional methods defined to act like a real construction
1410 environment with overridden values. It can wrap either a Base
1411 construction environment, or another OverrideEnvironment, which
1412 can in turn nest arbitrary OverrideEnvironments...
1414 Note that we do *not* call the underlying base class
1415 (SubsitutionEnvironment) initialization, because we get most of those
1416 from proxying the attributes of the subject construction environment.
1417 But because we subclass SubstitutionEnvironment, this class also
1418 has inherited arg2nodes() and subst*() methods; those methods can't
1419 be proxied because they need *this* object's methods to fetch the
1420 values from the overrides dictionary.
1423 __metaclass__ = SCons.Memoize.Memoized_Metaclass
1425 def __init__(self, subject, overrides={}):
1426 if __debug__: logInstanceCreation(self, 'OverrideEnvironment')
1427 self.__dict__['__subject'] = subject
1428 self.__dict__['overrides'] = overrides
1430 # Methods that make this class act like a proxy.
1431 def __getattr__(self, name):
1432 return getattr(self.__dict__['__subject'], name)
1433 def __setattr__(self, name, value):
1434 return setattr(self.__dict__['__subject'], name, value)
1436 # Methods that make this class act like a dictionary.
1437 def __getitem__(self, key):
1439 return self.__dict__['overrides'][key]
1441 return self.__dict__['__subject'].__getitem__(key)
1442 def __setitem__(self, key, value):
1443 if not SCons.Util.is_valid_construction_var(key):
1444 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
1445 self.__dict__['overrides'][key] = value
1446 def __delitem__(self, key):
1448 del self.__dict__['overrides'][key]
1451 return self.__dict__['__subject'].__delitem__(key)
1452 def get(self, key, default=None):
1453 """Emulates the get() method of dictionaries."""
1455 return self.__dict__['overrides'][key]
1457 return self.__dict__['__subject'].get(key, default)
1458 def has_key(self, key):
1460 self.__dict__['overrides'][key]
1463 return self.__dict__['__subject'].has_key(key)
1465 """Emulates the items() method of dictionaries."""
1466 return self.Dictionary().items()
1468 # Overridden private construction environment methods.
1469 def _update(self, dict):
1470 """Update an environment's values directly, bypassing the normal
1471 checks that occur when users try to set items.
1473 self.__dict__['overrides'].update(dict)
1476 return self.__dict__['__subject'].gvars()
1479 lvars = self.__dict__['__subject'].lvars()
1480 lvars.update(self.__dict__['overrides'])
1483 # Overridden public construction environment methods.
1484 def Replace(self, **kw):
1485 kw = copy_non_reserved_keywords(kw)
1486 self.__dict__['overrides'].update(our_deepcopy(kw))
1488 # The entry point that will be used by the external world
1489 # to refer to a construction environment. This allows the wrapper
1490 # interface to extend a construction environment for its own purposes
1491 # by subclassing SCons.Environment.Base and then assigning the
1492 # class to SCons.Environment.Environment.
1496 # An entry point for returning a proxy subclass instance that overrides
1497 # the subst*() methods so they don't actually perform construction
1498 # variable substitution. This is specifically intended to be the shim
1499 # layer in between global function calls (which don't want construction
1500 # variable substitution) and the DefaultEnvironment() (which would
1501 # substitute variables if left to its own devices)."""
1503 # We have to wrap this in a function that allows us to delay definition of
1504 # the class until it's necessary, so that when it subclasses Environment
1505 # it will pick up whatever Environment subclass the wrapper interface
1506 # might have assigned to SCons.Environment.Environment.
1508 def NoSubstitutionProxy(subject):
1509 class _NoSubstitutionProxy(Environment):
1510 def __init__(self, subject):
1511 self.__dict__['__subject'] = subject
1512 def __getattr__(self, name):
1513 return getattr(self.__dict__['__subject'], name)
1514 def __setattr__(self, name, value):
1515 return setattr(self.__dict__['__subject'], name, value)
1516 def raw_to_mode(self, dict):
1524 def subst(self, string, *args, **kwargs):
1526 def subst_kw(self, kw, *args, **kwargs):
1528 def subst_list(self, string, *args, **kwargs):
1529 nargs = (string, self,) + args
1532 self.raw_to_mode(nkw)
1533 return apply(SCons.Util.scons_subst_list, nargs, nkw)
1534 def subst_target_source(self, string, *args, **kwargs):
1535 nargs = (string, self,) + args
1538 self.raw_to_mode(nkw)
1539 return apply(SCons.Util.scons_subst, nargs, nkw)
1540 return _NoSubstitutionProxy(subject)
1542 if not SCons.Memoize.has_metaclass:
1544 class Base(SCons.Memoize.Memoizer, _Base):
1545 def __init__(self, *args, **kw):
1546 SCons.Memoize.Memoizer.__init__(self)
1547 apply(_Base.__init__, (self,)+args, kw)