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__"
43 from UserDict import UserDict
47 from SCons.Debug import logInstanceCreation
51 import SCons.Node.Alias
53 import SCons.Node.Python
57 import SCons.Sig.TimeStamp
71 # Pull UserError into the global name space for the benefit of
72 # Environment().SourceSignatures(), which has some import statements
73 # which seem to mess up its ability to reference SCons directly.
74 UserError = SCons.Errors.UserError
76 def installFunc(target, source, env):
77 """Install a source file into a target using the function specified
78 as the INSTALL construction variable."""
80 install = env['INSTALL']
82 raise SCons.Errors.UserError('Missing INSTALL construction variable.')
83 return install(target[0].path, source[0].path, env)
85 def installString(target, source, env):
86 s = env.get('INSTALLSTR', '')
88 return s(target[0].path, source[0].path, env)
90 return env.subst_target_source(s, 0, target, source)
92 installAction = SCons.Action.Action(installFunc, installString)
94 InstallBuilder = SCons.Builder.Builder(action=installAction,
95 name='InstallBuilder')
97 def alias_builder(env, target, source):
100 AliasBuilder = SCons.Builder.Builder(action = alias_builder,
101 target_factory = SCons.Node.Alias.default_ans.Alias,
102 source_factory = SCons.Node.FS.Entry,
108 """deepcopy lists and dictionaries, and just copy the reference
109 for everything else."""
110 if SCons.Util.is_Dict(x):
113 copy[key] = our_deepcopy(x[key])
114 elif SCons.Util.is_List(x):
115 copy = map(our_deepcopy, x)
117 copy = x.__class__(copy)
118 except AttributeError:
124 def apply_tools(env, tools, toolpath):
125 # Store the toolpath in the Environment.
126 if toolpath is not None:
127 env['toolpath'] = toolpath
131 # Filter out null tools from the list.
132 for tool in filter(None, tools):
133 if SCons.Util.is_List(tool) or type(tool)==type(()):
135 toolargs = tool[1] # should be a dict of kw args
136 tool = apply(env.Tool, [toolname], toolargs)
140 # These names are controlled by SCons; users should never set or override
141 # them. This warning can optionally be turned off, but scons will still
142 # ignore the illegal variable names even if it's off.
143 reserved_construction_var_names = \
144 ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
146 def copy_non_reserved_keywords(dict):
147 result = our_deepcopy(dict)
148 for k in result.keys():
149 if k in reserved_construction_var_names:
150 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
151 "Ignoring attempt to set reserved variable `%s'" % k)
155 def _set_reserved(env, key, value):
156 msg = "Ignoring attempt to set reserved variable `%s'" % key
157 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg)
159 def _set_BUILDERS(env, key, value):
165 env._dict[key] = BuilderDict(kwbd, env)
166 env._dict[key].update(value)
168 def _del_SCANNERS(env, key):
170 env.scanner_map_delete()
172 def _set_SCANNERS(env, key, value):
173 env._dict[key] = value
174 env.scanner_map_delete()
176 class BuilderWrapper:
177 """Wrapper class that associates an environment with a Builder at
179 def __init__(self, env, builder):
181 self.builder = builder
183 def __call__(self, target=None, source=_null, *args, **kw):
187 if not target is None and not SCons.Util.is_List(target):
189 if not source is None and not SCons.Util.is_List(source):
191 return apply(self.builder, (self.env, target, source) + args, kw)
193 # This allows a Builder to be executed directly
194 # through the Environment to which it's attached.
195 # In practice, we shouldn't need this, because
196 # builders actually get executed through a Node.
197 # But we do have a unit test for this, and can't
198 # yet rule out that it would be useful in the
199 # future, so leave it for now.
200 def execute(self, **kw):
202 apply(self.builder.execute, (), kw)
204 class BuilderDict(UserDict):
205 """This is a dictionary-like class used by an Environment to hold
206 the Builders. We need to do this because every time someone changes
207 the Builders in the Environment's BUILDERS dictionary, we must
208 update the Environment's attributes."""
209 def __init__(self, dict, env):
210 # Set self.env before calling the superclass initialization,
211 # because it will end up calling our other methods, which will
212 # need to point the values in this dictionary to self.env.
214 UserDict.__init__(self, dict)
216 def __setitem__(self, item, val):
217 UserDict.__setitem__(self, item, val)
219 self.setenvattr(item, val)
220 except AttributeError:
221 # Have to catch this because sometimes __setitem__ gets
222 # called out of __init__, when we don't have an env
223 # attribute yet, nor do we want one!
226 def setenvattr(self, item, val):
227 """Set the corresponding environment attribute for this Builder.
229 If the value is already a BuilderWrapper, we pull the builder
230 out of it and make another one, so that making a copy of an
231 existing BuilderDict is guaranteed separate wrappers for each
232 Builder + Environment pair."""
234 builder = val.builder
235 except AttributeError:
237 setattr(self.env, item, BuilderWrapper(self.env, builder))
239 def __delitem__(self, item):
240 UserDict.__delitem__(self, item)
241 delattr(self.env, item)
243 def update(self, dict):
244 for i, v in dict.items():
245 self.__setitem__(i, v)
247 class SubstitutionEnvironment:
248 """Base class for different flavors of construction environments.
250 This class contains a minimal set of methods that handle contruction
251 variable expansion and conversion of strings to Nodes, which may or
252 may not be actually useful as a stand-alone class. Which methods
253 ended up in this class is pretty arbitrary right now. They're
254 basically the ones which we've empirically determined are common to
255 the different construction environment subclasses, and most of the
256 others that use or touch the underlying dictionary of construction
259 Eventually, this class should contain all the methods that we
260 determine are necessary for a "minimal" interface to the build engine.
261 A full "native Python" SCons environment has gotten pretty heavyweight
262 with all of the methods and Tools and construction variables we've
263 jammed in there, so it would be nice to have a lighter weight
264 alternative for interfaces that don't need all of the bells and
265 whistles. (At some point, we'll also probably rename this class
266 "Base," since that more reflects what we want this class to become,
267 but because we've released comments that tell people to subclass
268 Environment.Base to create their own flavors of construction
269 environment, we'll save that for a future refactoring when this
270 class actually becomes useful.)
273 if SCons.Memoize.use_memoizer:
274 __metaclass__ = SCons.Memoize.Memoized_Metaclass
276 def __init__(self, **kw):
277 """Initialization of an underlying SubstitutionEnvironment class.
279 if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
280 self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS()
281 self.ans = SCons.Node.Alias.default_ans
282 self.lookup_list = SCons.Node.arg2nodes_lookups
283 self._dict = kw.copy()
287 def _init_special(self):
288 """Initial the dispatch tables for special handling of
289 special construction variables."""
290 self._special_del = {}
291 self._special_del['SCANNERS'] = _del_SCANNERS
293 self._special_set = {}
294 for key in reserved_construction_var_names:
295 self._special_set[key] = _set_reserved
296 self._special_set['BUILDERS'] = _set_BUILDERS
297 self._special_set['SCANNERS'] = _set_SCANNERS
299 def __cmp__(self, other):
300 return cmp(self._dict, other._dict)
302 def __delitem__(self, key):
303 special = self._special_del.get(key)
309 def __getitem__(self, key):
310 return self._dict[key]
312 def __setitem__(self, key, value):
313 special = self._special_set.get(key)
315 special(self, key, value)
317 if not SCons.Util.is_valid_construction_var(key):
318 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
319 self._dict[key] = value
321 def get(self, key, default=None):
322 "Emulates the get() method of dictionaries."""
323 return self._dict.get(key, default)
325 def has_key(self, key):
326 return self._dict.has_key(key)
329 return self._dict.items()
331 def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
332 if node_factory is _null:
333 node_factory = self.fs.File
334 if lookup_list is _null:
335 lookup_list = self.lookup_list
340 if SCons.Util.is_List(args):
341 args = SCons.Util.flatten(args)
347 if SCons.Util.is_String(v):
349 for l in lookup_list:
354 if SCons.Util.is_String(n):
355 n = self.subst(n, raw=1)
358 if SCons.Util.is_List(n):
363 v = node_factory(self.subst(v, raw=1))
364 if SCons.Util.is_List(v):
379 def subst(self, string, raw=0, target=None, source=None, conv=None):
380 """Recursively interpolates construction variables from the
381 Environment into the specified string, returning the expanded
382 result. Construction variables are specified by a $ prefix
383 in the string and begin with an initial underscore or
384 alphabetic character followed by any number of underscores
385 or alphanumeric characters. The construction variable names
386 may be surrounded by curly braces to separate the name from
391 lvars['__env__'] = self
392 return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
394 def subst_kw(self, kw, raw=0, target=None, source=None):
396 for k, v in kw.items():
397 k = self.subst(k, raw, target, source)
398 if SCons.Util.is_String(v):
399 v = self.subst(v, raw, target, source)
403 def subst_list(self, string, raw=0, target=None, source=None, conv=None):
404 """Calls through to SCons.Subst.scons_subst_list(). See
405 the documentation for that function."""
408 lvars['__env__'] = self
409 return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
411 def subst_path(self, path, target=None, source=None):
412 """Substitute a path list, turning EntryProxies into Nodes
413 and leaving Nodes (and other objects) as-is."""
415 if not SCons.Util.is_List(path):
419 """This is the "string conversion" routine that we have our
420 substitutions use to return Nodes, not strings. This relies
421 on the fact that an EntryProxy object has a get() method that
422 returns the underlying Node that it wraps, which is a bit of
423 architectural dependence that we might need to break or modify
424 in the future in response to additional requirements."""
427 except AttributeError:
435 if SCons.Util.is_String(p):
436 p = self.subst(p, target=target, source=source, conv=s)
437 if SCons.Util.is_List(p):
441 # We have an object plus a string, or multiple
442 # objects that we need to smush together. No choice
443 # but to make them into a string.
444 p = string.join(map(SCons.Util.to_String, p), '')
450 subst_target_source = subst
452 def backtick(self, command):
455 except AttributeError:
456 (tochild, fromchild, childerr) = os.popen3(self.subst(command))
458 err = childerr.read()
459 out = fromchild.read()
461 status = childerr.close()
463 p = popen2.Popen3(command, 1)
465 out = p.fromchild.read()
466 err = p.childerr.read()
470 sys.stderr.write(err)
473 if os.WIFEXITED(status):
474 status = os.WEXITSTATUS(status)
475 except AttributeError:
477 raise OSError("'%s' exited %s" % (command, status))
480 def Override(self, overrides):
482 Produce a modified environment whose variables are overriden by
483 the overrides dictionaries. "overrides" is a dictionary that
484 will override the variables of this environment.
486 This function is much more efficient than Clone() or creating
487 a new Environment because it doesn't copy the construction
488 environment dictionary, it just wraps the underlying construction
489 environment, and doesn't even create a wrapper object if there
493 o = copy_non_reserved_keywords(overrides)
495 for key, value in o.items():
496 overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
498 env = OverrideEnvironment(self, overrides)
503 def ParseFlags(self, *flags):
505 Parse the set of flags and return a dict with the flags placed
506 in the appropriate entry. The flags are treated as a typical
507 set of command-line flags for a GNU-like toolchain and used to
508 populate the entries in the dict immediately below. If one of
509 the flag strings begins with a bang (exclamation mark), it is
510 assumed to be a command and the rest of the string is executed;
511 the result of that evaluation is then added to the dict.
520 'FRAMEWORKPATH' : [],
528 # The use of the "me" parameter to provide our own name for
529 # recursion is an egregious hack to support Python 2.1 and before.
530 def do_parse(arg, me, self = self, dict = dict):
531 # if arg is a sequence, recurse with each element
535 if not SCons.Util.is_String(arg):
536 for t in arg: me(t, me)
539 # if arg is a command, execute it
541 arg = self.backtick(arg[1:])
543 # utility function to deal with -D option
544 def append_define(name, dict = dict):
545 t = string.split(name, '=')
547 dict['CPPDEFINES'].append(name)
549 dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
551 # Loop through the flags and add them to the appropriate option.
552 # This tries to strike a balance between checking for all possible
553 # flags and keeping the logic to a finite size, so it doesn't
554 # check for some that don't occur often. It particular, if the
555 # flag is not known to occur in a config script and there's a way
556 # of passing the flag to the right place (by wrapping it in a -W
557 # flag, for example) we don't check for it. Note that most
558 # preprocessor options are not handled, since unhandled options
559 # are placed in CCFLAGS, so unless the preprocessor is invoked
560 # separately, these flags will still get to the preprocessor.
561 # Other options not currently handled:
562 # -iqoutedir (preprocessor search path)
563 # -u symbol (linker undefined symbol)
564 # -s (linker strip files)
565 # -static* (linker static binding)
566 # -shared* (linker dynamic binding)
567 # -symbolic (linker global binding)
568 # -R dir (deprecated linker rpath)
569 # IBM compilers may also accept -qframeworkdir=foo
571 params = string.split(arg)
572 append_next_arg_to = None # for multi-word args
574 if append_next_arg_to:
575 if append_next_arg_to == 'CPPDEFINES':
577 elif append_next_arg_to == '-include':
578 t = ('-include', self.fs.File(arg))
579 dict['CCFLAGS'].append(t)
580 elif append_next_arg_to == '-isysroot':
581 t = ('-isysroot', arg)
582 dict['CCFLAGS'].append(t)
583 dict['LINKFLAGS'].append(t)
584 elif append_next_arg_to == '-arch':
586 dict['CCFLAGS'].append(t)
587 dict['LINKFLAGS'].append(t)
589 dict[append_next_arg_to].append(arg)
590 append_next_arg_to = None
591 elif not arg[0] in ['-', '+']:
592 dict['LIBS'].append(self.fs.File(arg))
593 elif arg[:2] == '-L':
595 dict['LIBPATH'].append(arg[2:])
597 append_next_arg_to = 'LIBPATH'
598 elif arg[:2] == '-l':
600 dict['LIBS'].append(arg[2:])
602 append_next_arg_to = 'LIBS'
603 elif arg[:2] == '-I':
605 dict['CPPPATH'].append(arg[2:])
607 append_next_arg_to = 'CPPPATH'
608 elif arg[:4] == '-Wa,':
609 dict['ASFLAGS'].append(arg[4:])
610 dict['CCFLAGS'].append(arg)
611 elif arg[:4] == '-Wl,':
612 if arg[:11] == '-Wl,-rpath=':
613 dict['RPATH'].append(arg[11:])
614 elif arg[:7] == '-Wl,-R,':
615 dict['RPATH'].append(arg[7:])
616 elif arg[:6] == '-Wl,-R':
617 dict['RPATH'].append(arg[6:])
619 dict['LINKFLAGS'].append(arg)
620 elif arg[:4] == '-Wp,':
621 dict['CPPFLAGS'].append(arg)
622 elif arg[:2] == '-D':
624 append_define(arg[2:])
626 appencd_next_arg_to = 'CPPDEFINES'
627 elif arg == '-framework':
628 append_next_arg_to = 'FRAMEWORKS'
629 elif arg[:14] == '-frameworkdir=':
630 dict['FRAMEWORKPATH'].append(arg[14:])
631 elif arg[:2] == '-F':
633 dict['FRAMEWORKPATH'].append(arg[2:])
635 append_next_arg_to = 'FRAMEWORKPATH'
636 elif arg == '-mno-cygwin':
637 dict['CCFLAGS'].append(arg)
638 dict['LINKFLAGS'].append(arg)
639 elif arg == '-mwindows':
640 dict['LINKFLAGS'].append(arg)
641 elif arg == '-pthread':
642 dict['CCFLAGS'].append(arg)
643 dict['LINKFLAGS'].append(arg)
644 elif arg[:5] == '-std=':
645 dict['CFLAGS'].append(arg) # C only
647 dict['CCFLAGS'].append(arg)
648 dict['LINKFLAGS'].append(arg)
649 elif arg in ['-include', '-isysroot', '-arch']:
650 append_next_arg_to = arg
652 dict['CCFLAGS'].append(arg)
655 do_parse(arg, do_parse)
658 def MergeFlags(self, args, unique=1):
660 Merge the dict in args into the construction variables. If args
661 is not a dict, it is converted into a dict using ParseFlags.
662 If unique is not set, the flags are appended rather than merged.
665 if not SCons.Util.is_Dict(args):
666 args = self.ParseFlags(args)
668 apply(self.Append, (), args)
670 for key, value in args.items():
680 elif not SCons.Util.is_List(orig):
684 if key[-4:] == 'PATH':
685 ### keep left-most occurence
690 ### keep right-most occurence
698 class Base(SubstitutionEnvironment):
699 """Base class for "real" construction Environments. These are the
700 primary objects used to communicate dependency and construction
701 information to the build engine.
703 Keyword arguments supplied when the construction Environment
704 is created are construction variables used to initialize the
708 if SCons.Memoize.use_memoizer:
709 __metaclass__ = SCons.Memoize.Memoized_Metaclass
711 memoizer_counters = []
713 #######################################################################
714 # This is THE class for interacting with the SCons build engine,
715 # and it contains a lot of stuff, so we're going to try to keep this
716 # a little organized by grouping the methods.
717 #######################################################################
719 #######################################################################
720 # Methods that make an Environment act like a dictionary. These have
721 # the expected standard names for Python mapping objects. Note that
722 # we don't actually make an Environment a subclass of UserDict for
723 # performance reasons. Note also that we only supply methods for
724 # dictionary functionality that we actually need and use.
725 #######################################################################
734 Initialization of a basic SCons construction environment,
735 including setting up special construction variables like BUILDER,
736 PLATFORM, etc., and searching for and applying available Tools.
738 Note that we do *not* call the underlying base class
739 (SubsitutionEnvironment) initialization, because we need to
740 initialize things in a very specific order that doesn't work
741 with the much simpler base class initialization.
743 if __debug__: logInstanceCreation(self, 'Environment.Base')
745 self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS()
746 self.ans = SCons.Node.Alias.default_ans
747 self.lookup_list = SCons.Node.arg2nodes_lookups
748 self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
751 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
754 platform = self._dict.get('PLATFORM', None)
756 platform = SCons.Platform.Platform()
757 if SCons.Util.is_String(platform):
758 platform = SCons.Platform.Platform(platform)
759 self._dict['PLATFORM'] = str(platform)
762 # Apply the passed-in variables and customizable options to the
763 # environment before calling the tools, because they may use
764 # some of them during initialization.
765 apply(self.Replace, (), kw)
768 keys = keys + options.keys()
774 save[k] = self._dict[k]
776 # No value may have been set if they tried to pass in a
777 # reserved variable name like TARGETS.
781 tools = self._dict.get('TOOLS', None)
784 apply_tools(self, tools, toolpath)
786 # Now restore the passed-in variables and customized options
787 # to the environment, since the values the user set explicitly
788 # should override any values set by the tools.
789 for key, val in save.items():
790 self._dict[key] = val
792 #######################################################################
793 # Utility methods that are primarily for internal use by SCons.
794 # These begin with lower-case letters.
795 #######################################################################
797 def get_builder(self, name):
798 """Fetch the builder with the specified name from the environment.
801 return self._dict['BUILDERS'][name]
805 def get_calculator(self):
807 module = self._calc_module
808 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
809 except AttributeError:
810 # Note that we're calling get_calculator() here, so the
811 # DefaultEnvironment() must have a _calc_module attribute
812 # to avoid infinite recursion.
813 c = SCons.Defaults.DefaultEnvironment().get_calculator()
816 def get_factory(self, factory, default='File'):
817 """Return a factory function for creating Nodes for this
818 construction environment.
822 is_node = issubclass(factory, SCons.Node.Node)
824 # The specified factory isn't a Node itself--it's
825 # most likely None, or possibly a callable.
829 # The specified factory is a Node (sub)class. Try to
830 # return the FS method that corresponds to the Node's
831 # name--that is, we return self.fs.Dir if they want a Dir,
832 # self.fs.File for a File, etc.
833 try: name = factory.__name__
834 except AttributeError: pass
837 # They passed us None, or we picked up a name from a specified
838 # class, so return the FS method. (Note that we *don't*
839 # use our own self.{Dir,File} methods because that would
840 # cause env.subst() to be called twice on the file name,
841 # interfering with files that have $$ in them.)
842 factory = getattr(self.fs, name)
845 memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
849 return self._memo['_gsm']
856 scanners = self._dict['SCANNERS']
860 # Reverse the scanner list so that, if multiple scanners
861 # claim they can scan the same suffix, earlier scanners
862 # in the list will overwrite later scanners, so that
863 # the result looks like a "first match" to the user.
864 if not SCons.Util.is_List(scanners):
865 scanners = [scanners]
867 scanners = scanners[:] # copy so reverse() doesn't mod original
869 for scanner in scanners:
870 for k in scanner.get_skeys(self):
873 self._memo['_gsm'] = result
877 def get_scanner(self, skey):
878 """Find the appropriate scanner given a key (usually a file suffix).
880 return self._gsm().get(skey)
882 def scanner_map_delete(self, kw=None):
883 """Delete the cached scanner map (if we need to).
886 del self._memo['_gsm']
890 def _update(self, dict):
891 """Update an environment's values directly, bypassing the normal
892 checks that occur when users try to set items.
894 self._dict.update(dict)
896 def use_build_signature(self):
898 return self._build_signature
899 except AttributeError:
900 b = SCons.Defaults.DefaultEnvironment()._build_signature
901 self._build_signature = b
904 #######################################################################
905 # Public methods for manipulating an Environment. These begin with
906 # upper-case letters. The essential characteristic of methods in
907 # this section is that they do *not* have corresponding same-named
908 # global functions. For example, a stand-alone Append() function
909 # makes no sense, because Append() is all about appending values to
910 # an Environment's construction variables.
911 #######################################################################
913 def Append(self, **kw):
914 """Append values to existing construction variables
917 kw = copy_non_reserved_keywords(kw)
918 for key, val in kw.items():
919 # It would be easier on the eyes to write this using
920 # "continue" statements whenever we finish processing an item,
921 # but Python 1.5.2 apparently doesn't let you use "continue"
922 # within try:-except: blocks, so we have to nest our code.
924 orig = self._dict[key]
926 # No existing variable in the environment, so just set
927 # it to the new value.
928 self._dict[key] = val
931 # Check if the original looks like a dictionary.
932 # If it is, we can't just try adding the value because
933 # dictionaries don't have __add__() methods, and
934 # things like UserList will incorrectly coerce the
935 # original dict to a list (which we don't want).
936 update_dict = orig.update
937 except AttributeError:
939 # Most straightforward: just try to add them
940 # together. This will work in most cases, when the
941 # original and new values are of compatible types.
942 self._dict[key] = orig + val
943 except (KeyError, TypeError):
945 # Check if the original is a list.
946 add_to_orig = orig.append
947 except AttributeError:
948 # The original isn't a list, but the new
949 # value is (by process of elimination),
950 # so insert the original in the new value
951 # (if there's one to insert) and replace
952 # the variable with it.
955 self._dict[key] = val
957 # The original is a list, so append the new
958 # value to it (if there's a value to append).
962 # The original looks like a dictionary, so update it
963 # based on what we think the value looks like.
964 if SCons.Util.is_List(val):
970 except (AttributeError, TypeError, ValueError):
972 self.scanner_map_delete(kw)
974 def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
975 """Append path elements to the path 'name' in the 'ENV'
976 dictionary for this environment. Will only add any particular
977 path once, and will normpath and normcase all paths to help
978 assure this. This can also handle the case where the env
979 variable is a list instead of a string.
983 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
984 orig = self._dict[envname][name]
986 nv = SCons.Util.AppendPath(orig, newpath, sep)
988 if not self._dict.has_key(envname):
989 self._dict[envname] = {}
991 self._dict[envname][name] = nv
993 def AppendUnique(self, **kw):
994 """Append values to existing construction variables
995 in an Environment, if they're not already there.
997 kw = copy_non_reserved_keywords(kw)
998 for key, val in kw.items():
999 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1000 self._dict[key] = val
1001 elif SCons.Util.is_Dict(self._dict[key]) and \
1002 SCons.Util.is_Dict(val):
1003 self._dict[key].update(val)
1004 elif SCons.Util.is_List(val):
1005 dk = self._dict[key]
1006 if not SCons.Util.is_List(dk):
1008 val = filter(lambda x, dk=dk: x not in dk, val)
1009 self._dict[key] = dk + val
1011 dk = self._dict[key]
1012 if SCons.Util.is_List(dk):
1013 # By elimination, val is not a list. Since dk is a
1014 # list, wrap val in a list first.
1016 self._dict[key] = dk + [val]
1018 self._dict[key] = self._dict[key] + val
1019 self.scanner_map_delete(kw)
1021 def Clone(self, tools=[], toolpath=None, **kw):
1022 """Return a copy of a construction Environment. The
1023 copy is like a Python "deep copy"--that is, independent
1024 copies are made recursively of each objects--except that
1025 a reference is copied when an object is not deep-copyable
1026 (like a function). There are no references to any mutable
1027 objects in the original Environment.
1029 clone = copy.copy(self)
1030 clone._dict = our_deepcopy(self._dict)
1032 cbd = clone._dict['BUILDERS']
1033 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
1039 apply_tools(clone, tools, toolpath)
1041 # Apply passed-in variables after the new tools.
1042 kw = copy_non_reserved_keywords(kw)
1044 for key, value in kw.items():
1045 new[key] = SCons.Subst.scons_subst_once(value, self, key)
1046 apply(clone.Replace, (), new)
1047 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
1050 def Copy(self, *args, **kw):
1051 return apply(self.Clone, args, kw)
1053 def Detect(self, progs):
1054 """Return the first available program in progs.
1056 if not SCons.Util.is_List(progs):
1059 path = self.WhereIs(prog)
1060 if path: return prog
1063 def Dictionary(self, *args):
1066 dlist = map(lambda x, s=self: s._dict[x], args)
1071 def Dump(self, key = None):
1073 Using the standard Python pretty printer, dump the contents of the
1074 scons build environment to stdout.
1076 If the key passed in is anything other than None, then that will
1077 be used as an index into the build environment dictionary and
1078 whatever is found there will be fed into the pretty printer. Note
1079 that this key is case sensitive.
1082 pp = pprint.PrettyPrinter(indent=2)
1084 dict = self.Dictionary(key)
1086 dict = self.Dictionary()
1087 return pp.pformat(dict)
1089 def FindIxes(self, paths, prefix, suffix):
1091 Search a list of paths for something that matches the prefix and suffix.
1093 paths - the list of paths or nodes.
1094 prefix - construction variable for the prefix.
1095 suffix - construction variable for the suffix.
1098 suffix = self.subst('$'+suffix)
1099 prefix = self.subst('$'+prefix)
1102 dir,name = os.path.split(str(path))
1103 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
1106 def ParseConfig(self, command, function=None, unique=1):
1108 Use the specified function to parse the output of the command
1109 in order to modify the current environment. The 'command' can
1110 be a string or a list of strings representing a command and
1111 its arguments. 'Function' is an optional argument that takes
1112 the environment, the output of the command, and the unique flag.
1113 If no function is specified, MergeFlags, which treats the output
1114 as the result of a typical 'X-config' command (i.e. gtk-config),
1115 will merge the output into the appropriate variables.
1117 if function is None:
1118 def parse_conf(env, cmd, unique=unique):
1119 return env.MergeFlags(cmd, unique)
1120 function = parse_conf
1121 if SCons.Util.is_List(command):
1122 command = string.join(command)
1123 command = self.subst(command)
1124 return function(self, self.backtick(command))
1126 def ParseDepends(self, filename, must_exist=None, only_one=0):
1128 Parse a mkdep-style file for explicit dependencies. This is
1129 completely abusable, and should be unnecessary in the "normal"
1130 case of proper SCons configuration, but it may help make
1131 the transition from a Make hierarchy easier for some people
1132 to swallow. It can also be genuinely useful when using a tool
1133 that can write a .d file, but for which writing a scanner would
1136 filename = self.subst(filename)
1138 fp = open(filename, 'r')
1143 lines = SCons.Util.LogicalLines(fp).readlines()
1144 lines = filter(lambda l: l[0] != '#', lines)
1148 target, depends = string.split(line, ':', 1)
1149 except (AttributeError, TypeError, ValueError):
1150 # Python 1.5.2 throws TypeError if line isn't a string,
1151 # Python 2.x throws AttributeError because it tries
1152 # to call line.split(). Either can throw ValueError
1153 # if the line doesn't split into two or more elements.
1156 tdlist.append((string.split(target), string.split(depends)))
1158 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
1159 if len(targets) > 1:
1160 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
1161 for target, depends in tdlist:
1162 self.Depends(target, depends)
1164 def Platform(self, platform):
1165 platform = self.subst(platform)
1166 return SCons.Platform.Platform(platform)(self)
1168 def Prepend(self, **kw):
1169 """Prepend values to existing construction variables
1172 kw = copy_non_reserved_keywords(kw)
1173 for key, val in kw.items():
1174 # It would be easier on the eyes to write this using
1175 # "continue" statements whenever we finish processing an item,
1176 # but Python 1.5.2 apparently doesn't let you use "continue"
1177 # within try:-except: blocks, so we have to nest our code.
1179 orig = self._dict[key]
1181 # No existing variable in the environment, so just set
1182 # it to the new value.
1183 self._dict[key] = val
1186 # Check if the original looks like a dictionary.
1187 # If it is, we can't just try adding the value because
1188 # dictionaries don't have __add__() methods, and
1189 # things like UserList will incorrectly coerce the
1190 # original dict to a list (which we don't want).
1191 update_dict = orig.update
1192 except AttributeError:
1194 # Most straightforward: just try to add them
1195 # together. This will work in most cases, when the
1196 # original and new values are of compatible types.
1197 self._dict[key] = val + orig
1198 except (KeyError, TypeError):
1200 # Check if the added value is a list.
1201 add_to_val = val.append
1202 except AttributeError:
1203 # The added value isn't a list, but the
1204 # original is (by process of elimination),
1205 # so insert the the new value in the original
1206 # (if there's one to insert).
1210 # The added value is a list, so append
1211 # the original to it (if there's a value
1215 self._dict[key] = val
1217 # The original looks like a dictionary, so update it
1218 # based on what we think the value looks like.
1219 if SCons.Util.is_List(val):
1225 except (AttributeError, TypeError, ValueError):
1227 self.scanner_map_delete(kw)
1229 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
1230 """Prepend path elements to the path 'name' in the 'ENV'
1231 dictionary for this environment. Will only add any particular
1232 path once, and will normpath and normcase all paths to help
1233 assure this. This can also handle the case where the env
1234 variable is a list instead of a string.
1238 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1239 orig = self._dict[envname][name]
1241 nv = SCons.Util.PrependPath(orig, newpath, sep)
1243 if not self._dict.has_key(envname):
1244 self._dict[envname] = {}
1246 self._dict[envname][name] = nv
1248 def PrependUnique(self, **kw):
1249 """Append values to existing construction variables
1250 in an Environment, if they're not already there.
1252 kw = copy_non_reserved_keywords(kw)
1253 for key, val in kw.items():
1254 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1255 self._dict[key] = val
1256 elif SCons.Util.is_Dict(self._dict[key]) and \
1257 SCons.Util.is_Dict(val):
1258 self._dict[key].update(val)
1259 elif SCons.Util.is_List(val):
1260 dk = self._dict[key]
1261 if not SCons.Util.is_List(dk):
1263 val = filter(lambda x, dk=dk: x not in dk, val)
1264 self._dict[key] = val + dk
1266 dk = self._dict[key]
1267 if SCons.Util.is_List(dk):
1268 # By elimination, val is not a list. Since dk is a
1269 # list, wrap val in a list first.
1271 self._dict[key] = [val] + dk
1273 self._dict[key] = val + dk
1274 self.scanner_map_delete(kw)
1276 def Replace(self, **kw):
1277 """Replace existing construction variables in an Environment
1278 with new construction variables and/or values.
1281 kwbd = our_deepcopy(kw['BUILDERS'])
1283 self.__setitem__('BUILDERS', kwbd)
1286 kw = copy_non_reserved_keywords(kw)
1287 self._update(our_deepcopy(kw))
1288 self.scanner_map_delete(kw)
1290 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1292 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1294 env - Environment used to interpolate variables.
1295 path - the path that will be modified.
1296 old_prefix - construction variable for the old prefix.
1297 old_suffix - construction variable for the old suffix.
1298 new_prefix - construction variable for the new prefix.
1299 new_suffix - construction variable for the new suffix.
1301 old_prefix = self.subst('$'+old_prefix)
1302 old_suffix = self.subst('$'+old_suffix)
1304 new_prefix = self.subst('$'+new_prefix)
1305 new_suffix = self.subst('$'+new_suffix)
1307 dir,name = os.path.split(str(path))
1308 if name[:len(old_prefix)] == old_prefix:
1309 name = name[len(old_prefix):]
1310 if name[-len(old_suffix):] == old_suffix:
1311 name = name[:-len(old_suffix)]
1312 return os.path.join(dir, new_prefix+name+new_suffix)
1314 def SetDefault(self, **kw):
1316 if self._dict.has_key(k):
1318 apply(self.Replace, (), kw)
1320 def Tool(self, tool, toolpath=None, **kw):
1321 if SCons.Util.is_String(tool):
1322 tool = self.subst(tool)
1323 if toolpath is None:
1324 toolpath = self.get('toolpath', [])
1325 toolpath = map(self.subst, toolpath)
1326 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
1329 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1330 """Find prog in the path.
1334 path = self['ENV']['PATH']
1337 elif SCons.Util.is_String(path):
1338 path = self.subst(path)
1341 pathext = self['ENV']['PATHEXT']
1344 elif SCons.Util.is_String(pathext):
1345 pathext = self.subst(pathext)
1346 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1347 if path: return path
1350 #######################################################################
1351 # Public methods for doing real "SCons stuff" (manipulating
1352 # dependencies, setting attributes on targets, etc.). These begin
1353 # with upper-case letters. The essential characteristic of methods
1354 # in this section is that they all *should* have corresponding
1355 # same-named global functions.
1356 #######################################################################
1358 def Action(self, *args, **kw):
1359 def subst_string(a, self=self):
1360 if SCons.Util.is_String(a):
1363 nargs = map(subst_string, args)
1364 nkw = self.subst_kw(kw)
1365 return apply(SCons.Action.Action, nargs, nkw)
1367 def AddPreAction(self, files, action):
1368 nodes = self.arg2nodes(files, self.fs.Entry)
1369 action = SCons.Action.Action(action)
1371 for executor in map(lambda n: n.get_executor(), nodes):
1373 for executor in uniq.keys():
1374 executor.add_pre_action(action)
1377 def AddPostAction(self, files, action):
1378 nodes = self.arg2nodes(files, self.fs.Entry)
1379 action = SCons.Action.Action(action)
1381 for executor in map(lambda n: n.get_executor(), nodes):
1383 for executor in uniq.keys():
1384 executor.add_post_action(action)
1387 def Alias(self, target, source=[], action=None, **kw):
1388 tlist = self.arg2nodes(target, self.ans.Alias)
1389 if not SCons.Util.is_List(source):
1391 source = filter(None, source)
1395 # There are no source files and no action, so just
1396 # return a target list of classic Alias Nodes, without
1397 # any builder. The externally visible effect is that
1398 # this will make the wrapping Script.BuildTask class
1399 # say that there's "Nothing to be done" for this Alias,
1400 # instead of that it's "up to date."
1403 # No action, but there are sources. Re-call all the target
1404 # builders to add the sources to each target.
1407 bld = t.get_builder(AliasBuilder)
1408 result.extend(bld(self, t, source))
1411 nkw = self.subst_kw(kw)
1413 'action' : SCons.Action.Action(action),
1414 'source_factory' : self.fs.Entry,
1416 'is_explicit' : None,
1418 bld = apply(SCons.Builder.Builder, (), nkw)
1420 # Apply the Builder separately to each target so that the Aliases
1421 # stay separate. If we did one "normal" Builder call with the
1422 # whole target list, then all of the target Aliases would be
1423 # associated under a single Executor.
1426 # Calling the convert() method will cause a new Executor to be
1427 # created from scratch, so we have to explicitly initialize
1428 # it with the target's existing sources, plus our new ones,
1429 # so nothing gets lost.
1431 if b is None or b is AliasBuilder:
1434 nkw['action'] = b.action + action
1435 b = apply(SCons.Builder.Builder, (), nkw)
1437 result.extend(b(self, t, t.sources + source))
1440 def AlwaysBuild(self, *targets):
1443 tlist.extend(self.arg2nodes(t, self.fs.File))
1445 t.set_always_build()
1448 def BuildDir(self, build_dir, src_dir, duplicate=1):
1449 build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
1450 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1451 self.fs.BuildDir(build_dir, src_dir, duplicate)
1453 def Builder(self, **kw):
1454 nkw = self.subst_kw(kw)
1455 return apply(SCons.Builder.Builder, [], nkw)
1457 def CacheDir(self, path):
1458 self.fs.CacheDir(self.subst(path))
1460 def Clean(self, targets, files):
1462 tlist = self.arg2nodes(targets, self.fs.Entry)
1463 flist = self.arg2nodes(files, self.fs.Entry)
1466 CleanTargets[t].extend(flist)
1468 CleanTargets[t] = flist
1470 def Configure(self, *args, **kw):
1473 nargs = nargs + self.subst_list(args)[0]
1474 nkw = self.subst_kw(kw)
1475 nkw['_depth'] = kw.get('_depth', 0) + 1
1477 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1480 return apply(SCons.SConf.SConf, nargs, nkw)
1482 def Command(self, target, source, action, **kw):
1483 """Builds the supplied target files from the supplied
1484 source files using the supplied action. Action may
1485 be any type that the Builder constructor will accept
1489 'target_factory' : self.fs.Entry,
1490 'source_factory' : self.fs.Entry,
1492 try: bkw['source_scanner'] = kw['source_scanner']
1493 except KeyError: pass
1494 else: del kw['source_scanner']
1495 bld = apply(SCons.Builder.Builder, (), bkw)
1496 return apply(bld, (self, target, source), kw)
1498 def Depends(self, target, dependency):
1499 """Explicity specify that 'target's depend on 'dependency'."""
1500 tlist = self.arg2nodes(target, self.fs.Entry)
1501 dlist = self.arg2nodes(dependency, self.fs.Entry)
1503 t.add_dependency(dlist)
1506 def Dir(self, name, *args, **kw):
1509 return apply(self.fs.Dir, (self.subst(name),) + args, kw)
1511 def NoClean(self, *targets):
1512 """Tags a target so that it will not be cleaned by -c"""
1515 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1520 def Entry(self, name, *args, **kw):
1523 return apply(self.fs.Entry, (self.subst(name),) + args, kw)
1525 def Environment(self, **kw):
1526 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1528 def Execute(self, action, *args, **kw):
1529 """Directly execute an action through an Environment
1531 action = apply(self.Action, (action,) + args, kw)
1532 return action([], [], self)
1534 def File(self, name, *args, **kw):
1537 return apply(self.fs.File, (self.subst(name),) + args, kw)
1539 def FindFile(self, file, dirs):
1540 file = self.subst(file)
1541 nodes = self.arg2nodes(dirs, self.fs.Dir)
1542 return SCons.Node.FS.find_file(file, tuple(nodes))
1544 def Flatten(self, sequence):
1545 return SCons.Util.flatten(sequence)
1547 def GetBuildPath(self, files):
1548 result = map(str, self.arg2nodes(files, self.fs.Entry))
1549 if SCons.Util.is_List(files):
1554 def Ignore(self, target, dependency):
1555 """Ignore a dependency."""
1556 tlist = self.arg2nodes(target, self.fs.Entry)
1557 dlist = self.arg2nodes(dependency, self.fs.Entry)
1562 def Install(self, dir, source):
1563 """Install specified files in the given directory."""
1565 dnodes = self.arg2nodes(dir, self.fs.Dir)
1567 fmt = "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?"
1568 raise SCons.Errors.UserError, fmt % str(dir)
1570 sources = self.arg2nodes(source, self.fs.Entry)
1572 if SCons.Util.is_List(source):
1573 s = repr(map(str, source))
1576 fmt = "Source `%s' of Install() is neither a file nor a directory. Install() source must be one or more files or directories"
1577 raise SCons.Errors.UserError, fmt % s
1579 for dnode in dnodes:
1581 target = self.fs.Entry(src.name, dnode)
1582 tgt.extend(InstallBuilder(self, target, src))
1585 def InstallAs(self, target, source):
1586 """Install sources as targets."""
1587 sources = self.arg2nodes(source, self.fs.Entry)
1588 targets = self.arg2nodes(target, self.fs.Entry)
1589 if len(sources) != len(targets):
1590 if not SCons.Util.is_List(target):
1592 if not SCons.Util.is_List(source):
1594 t = repr(map(str, target))
1595 s = repr(map(str, source))
1596 fmt = "Target (%s) and source (%s) lists of InstallAs() must be the same length."
1597 raise SCons.Errors.UserError, fmt % (t, s)
1599 for src, tgt in map(lambda x, y: (x, y), sources, targets):
1600 result.extend(InstallBuilder(self, tgt, src))
1603 def Literal(self, string):
1604 return SCons.Subst.Literal(string)
1606 def Local(self, *targets):
1608 for targ in targets:
1609 if isinstance(targ, SCons.Node.Node):
1613 for t in self.arg2nodes(targ, self.fs.Entry):
1618 def Precious(self, *targets):
1621 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1626 def Repository(self, *dirs, **kw):
1627 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1628 apply(self.fs.Repository, dirs, kw)
1630 def Scanner(self, *args, **kw):
1633 if SCons.Util.is_String(arg):
1634 arg = self.subst(arg)
1636 nkw = self.subst_kw(kw)
1637 return apply(SCons.Scanner.Scanner, nargs, nkw)
1639 def SConsignFile(self, name=".sconsign", dbm_module=None):
1640 if not name is None:
1641 name = self.subst(name)
1642 if not os.path.isabs(name):
1643 name = os.path.join(str(self.fs.SConstruct_dir), name)
1644 SCons.SConsign.File(name, dbm_module)
1646 def SideEffect(self, side_effect, target):
1647 """Tell scons that side_effects are built as side
1648 effects of building targets."""
1649 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1650 targets = self.arg2nodes(target, self.fs.Entry)
1652 for side_effect in side_effects:
1653 if side_effect.multiple_side_effect_has_builder():
1654 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1655 side_effect.add_source(targets)
1656 side_effect.side_effect = 1
1657 self.Precious(side_effect)
1658 for target in targets:
1659 target.side_effects.append(side_effect)
1662 def SourceCode(self, entry, builder):
1663 """Arrange for a source code builder for (part of) a tree."""
1664 entries = self.arg2nodes(entry, self.fs.Entry)
1665 for entry in entries:
1666 entry.set_src_builder(builder)
1669 def SourceSignatures(self, type):
1670 type = self.subst(type)
1673 import SCons.Sig.MD5
1675 msg = "No MD5 module available, using time stamps"
1676 SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg)
1677 import SCons.Sig.TimeStamp
1678 self._calc_module = SCons.Sig.TimeStamp
1680 self._calc_module = SCons.Sig.MD5
1681 elif type == 'timestamp':
1682 import SCons.Sig.TimeStamp
1683 self._calc_module = SCons.Sig.TimeStamp
1685 raise UserError, "Unknown source signature type '%s'"%type
1687 def Split(self, arg):
1688 """This function converts a string or list into a list of strings
1689 or Nodes. This makes things easier for users by allowing files to
1690 be specified as a white-space separated list to be split.
1691 The input rules are:
1692 - A single string containing names separated by spaces. These will be
1693 split apart at the spaces.
1694 - A single Node instance
1695 - A list containing either strings or Node instances. Any strings
1696 in the list are not split at spaces.
1697 In all cases, the function returns a list of Nodes and strings."""
1698 if SCons.Util.is_List(arg):
1699 return map(self.subst, arg)
1700 elif SCons.Util.is_String(arg):
1701 return string.split(self.subst(arg))
1703 return [self.subst(arg)]
1705 def TargetSignatures(self, type):
1706 type = self.subst(type)
1708 self._build_signature = 1
1709 elif type == 'content':
1710 self._build_signature = 0
1712 raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1714 def Value(self, value, built_value=None):
1717 return SCons.Node.Python.Value(value, built_value)
1719 class OverrideEnvironment(Base):
1720 """A proxy that overrides variables in a wrapped construction
1721 environment by returning values from an overrides dictionary in
1722 preference to values from the underlying subject environment.
1724 This is a lightweight (I hope) proxy that passes through most use of
1725 attributes to the underlying Environment.Base class, but has just
1726 enough additional methods defined to act like a real construction
1727 environment with overridden values. It can wrap either a Base
1728 construction environment, or another OverrideEnvironment, which
1729 can in turn nest arbitrary OverrideEnvironments...
1731 Note that we do *not* call the underlying base class
1732 (SubsitutionEnvironment) initialization, because we get most of those
1733 from proxying the attributes of the subject construction environment.
1734 But because we subclass SubstitutionEnvironment, this class also
1735 has inherited arg2nodes() and subst*() methods; those methods can't
1736 be proxied because they need *this* object's methods to fetch the
1737 values from the overrides dictionary.
1740 if SCons.Memoize.use_memoizer:
1741 __metaclass__ = SCons.Memoize.Memoized_Metaclass
1743 def __init__(self, subject, overrides={}):
1744 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
1745 self.__dict__['__subject'] = subject
1746 self.__dict__['overrides'] = overrides
1748 # Methods that make this class act like a proxy.
1749 def __getattr__(self, name):
1750 return getattr(self.__dict__['__subject'], name)
1751 def __setattr__(self, name, value):
1752 return setattr(self.__dict__['__subject'], name, value)
1754 # Methods that make this class act like a dictionary.
1755 def __getitem__(self, key):
1757 return self.__dict__['overrides'][key]
1759 return self.__dict__['__subject'].__getitem__(key)
1760 def __setitem__(self, key, value):
1761 if not SCons.Util.is_valid_construction_var(key):
1762 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
1763 self.__dict__['overrides'][key] = value
1764 def __delitem__(self, key):
1766 del self.__dict__['overrides'][key]
1772 result = self.__dict__['__subject'].__delitem__(key)
1778 def get(self, key, default=None):
1779 """Emulates the get() method of dictionaries."""
1781 return self.__dict__['overrides'][key]
1783 return self.__dict__['__subject'].get(key, default)
1784 def has_key(self, key):
1786 self.__dict__['overrides'][key]
1789 return self.__dict__['__subject'].has_key(key)
1790 def Dictionary(self):
1791 """Emulates the items() method of dictionaries."""
1792 d = self.__dict__['__subject'].Dictionary().copy()
1793 d.update(self.__dict__['overrides'])
1796 """Emulates the items() method of dictionaries."""
1797 return self.Dictionary().items()
1799 # Overridden private construction environment methods.
1800 def _update(self, dict):
1801 """Update an environment's values directly, bypassing the normal
1802 checks that occur when users try to set items.
1804 self.__dict__['overrides'].update(dict)
1807 return self.__dict__['__subject'].gvars()
1810 lvars = self.__dict__['__subject'].lvars()
1811 lvars.update(self.__dict__['overrides'])
1814 # Overridden public construction environment methods.
1815 def Replace(self, **kw):
1816 kw = copy_non_reserved_keywords(kw)
1817 self.__dict__['overrides'].update(our_deepcopy(kw))
1819 # The entry point that will be used by the external world
1820 # to refer to a construction environment. This allows the wrapper
1821 # interface to extend a construction environment for its own purposes
1822 # by subclassing SCons.Environment.Base and then assigning the
1823 # class to SCons.Environment.Environment.
1827 # An entry point for returning a proxy subclass instance that overrides
1828 # the subst*() methods so they don't actually perform construction
1829 # variable substitution. This is specifically intended to be the shim
1830 # layer in between global function calls (which don't want construction
1831 # variable substitution) and the DefaultEnvironment() (which would
1832 # substitute variables if left to its own devices)."""
1834 # We have to wrap this in a function that allows us to delay definition of
1835 # the class until it's necessary, so that when it subclasses Environment
1836 # it will pick up whatever Environment subclass the wrapper interface
1837 # might have assigned to SCons.Environment.Environment.
1839 def NoSubstitutionProxy(subject):
1840 class _NoSubstitutionProxy(Environment):
1841 def __init__(self, subject):
1842 self.__dict__['__subject'] = subject
1843 def __getattr__(self, name):
1844 return getattr(self.__dict__['__subject'], name)
1845 def __setattr__(self, name, value):
1846 return setattr(self.__dict__['__subject'], name, value)
1847 def raw_to_mode(self, dict):
1855 def subst(self, string, *args, **kwargs):
1857 def subst_kw(self, kw, *args, **kwargs):
1859 def subst_list(self, string, *args, **kwargs):
1860 nargs = (string, self,) + args
1863 self.raw_to_mode(nkw)
1864 return apply(SCons.Subst.scons_subst_list, nargs, nkw)
1865 def subst_target_source(self, string, *args, **kwargs):
1866 nargs = (string, self,) + args
1869 self.raw_to_mode(nkw)
1870 return apply(SCons.Subst.scons_subst, nargs, nkw)
1871 return _NoSubstitutionProxy(subject)