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
51 import SCons.Node.Alias
53 import SCons.Node.Python
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 s = env.get('INSTALLSTR', '')
87 return s(target[0].path, source[0].path, env)
89 return env.subst_target_source(s, 0, target, source)
91 installAction = SCons.Action.Action(installFunc, installString)
93 InstallBuilder = SCons.Builder.Builder(action=installAction,
94 name='InstallBuilder')
96 def alias_builder(env, target, source):
99 AliasBuilder = SCons.Builder.Builder(action = alias_builder,
100 target_factory = SCons.Node.Alias.default_ans.Alias,
101 source_factory = SCons.Node.FS.Entry,
107 """deepcopy lists and dictionaries, and just copy the reference
108 for everything else."""
109 if SCons.Util.is_Dict(x):
112 copy[key] = our_deepcopy(x[key])
113 elif SCons.Util.is_List(x):
114 copy = map(our_deepcopy, x)
116 copy = x.__class__(copy)
117 except AttributeError:
123 def apply_tools(env, tools, toolpath):
124 # Store the toolpath in the Environment.
125 if toolpath is not None:
126 env['toolpath'] = toolpath
130 # Filter out null tools from the list.
131 for tool in filter(None, tools):
132 if SCons.Util.is_List(tool) or type(tool)==type(()):
134 toolargs = tool[1] # should be a dict of kw args
135 tool = apply(env.Tool, [toolname], toolargs)
139 # These names are controlled by SCons; users should never set or override
140 # them. This warning can optionally be turned off, but scons will still
141 # ignore the illegal variable names even if it's off.
142 reserved_construction_var_names = \
143 ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
145 def copy_non_reserved_keywords(dict):
146 result = our_deepcopy(dict)
147 for k in result.keys():
148 if k in reserved_construction_var_names:
149 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
150 "Ignoring attempt to set reserved variable `%s'" % k)
154 def _set_reserved(env, key, value):
155 msg = "Ignoring attempt to set reserved variable `%s'" % key
156 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg)
158 def _set_BUILDERS(env, key, value):
164 env._dict[key] = BuilderDict(kwbd, env)
165 env._dict[key].update(value)
167 def _del_SCANNERS(env, key):
169 env.scanner_map_delete()
171 def _set_SCANNERS(env, key, value):
172 env._dict[key] = value
173 env.scanner_map_delete()
175 class BuilderWrapper:
176 """Wrapper class that associates an environment with a Builder at
178 def __init__(self, env, builder):
180 self.builder = builder
182 def __call__(self, target=None, source=_null, *args, **kw):
186 if not target is None and not SCons.Util.is_List(target):
188 if not source is None and not SCons.Util.is_List(source):
190 return apply(self.builder, (self.env, target, source) + args, kw)
192 # This allows a Builder to be executed directly
193 # through the Environment to which it's attached.
194 # In practice, we shouldn't need this, because
195 # builders actually get executed through a Node.
196 # But we do have a unit test for this, and can't
197 # yet rule out that it would be useful in the
198 # future, so leave it for now.
199 def execute(self, **kw):
201 apply(self.builder.execute, (), kw)
203 class BuilderDict(UserDict):
204 """This is a dictionary-like class used by an Environment to hold
205 the Builders. We need to do this because every time someone changes
206 the Builders in the Environment's BUILDERS dictionary, we must
207 update the Environment's attributes."""
208 def __init__(self, dict, env):
209 # Set self.env before calling the superclass initialization,
210 # because it will end up calling our other methods, which will
211 # need to point the values in this dictionary to self.env.
213 UserDict.__init__(self, dict)
215 def __setitem__(self, item, val):
216 UserDict.__setitem__(self, item, val)
218 self.setenvattr(item, val)
219 except AttributeError:
220 # Have to catch this because sometimes __setitem__ gets
221 # called out of __init__, when we don't have an env
222 # attribute yet, nor do we want one!
225 def setenvattr(self, item, val):
226 """Set the corresponding environment attribute for this Builder.
228 If the value is already a BuilderWrapper, we pull the builder
229 out of it and make another one, so that making a copy of an
230 existing BuilderDict is guaranteed separate wrappers for each
231 Builder + Environment pair."""
233 builder = val.builder
234 except AttributeError:
236 setattr(self.env, item, BuilderWrapper(self.env, builder))
238 def __delitem__(self, item):
239 UserDict.__delitem__(self, item)
240 delattr(self.env, item)
242 def update(self, dict):
243 for i, v in dict.items():
244 self.__setitem__(i, v)
246 class SubstitutionEnvironment:
247 """Base class for different flavors of construction environments.
249 This class contains a minimal set of methods that handle contruction
250 variable expansion and conversion of strings to Nodes, which may or
251 may not be actually useful as a stand-alone class. Which methods
252 ended up in this class is pretty arbitrary right now. They're
253 basically the ones which we've empirically determined are common to
254 the different construction environment subclasses, and most of the
255 others that use or touch the underlying dictionary of construction
258 Eventually, this class should contain all the methods that we
259 determine are necessary for a "minimal" interface to the build engine.
260 A full "native Python" SCons environment has gotten pretty heavyweight
261 with all of the methods and Tools and construction variables we've
262 jammed in there, so it would be nice to have a lighter weight
263 alternative for interfaces that don't need all of the bells and
264 whistles. (At some point, we'll also probably rename this class
265 "Base," since that more reflects what we want this class to become,
266 but because we've released comments that tell people to subclass
267 Environment.Base to create their own flavors of construction
268 environment, we'll save that for a future refactoring when this
269 class actually becomes useful.)
272 if SCons.Memoize.use_memoizer:
273 __metaclass__ = SCons.Memoize.Memoized_Metaclass
275 def __init__(self, **kw):
276 """Initialization of an underlying SubstitutionEnvironment class.
278 if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
279 self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS()
280 self.ans = SCons.Node.Alias.default_ans
281 self.lookup_list = SCons.Node.arg2nodes_lookups
282 self._dict = kw.copy()
286 def _init_special(self):
287 """Initial the dispatch tables for special handling of
288 special construction variables."""
289 self._special_del = {}
290 self._special_del['SCANNERS'] = _del_SCANNERS
292 self._special_set = {}
293 for key in reserved_construction_var_names:
294 self._special_set[key] = _set_reserved
295 self._special_set['BUILDERS'] = _set_BUILDERS
296 self._special_set['SCANNERS'] = _set_SCANNERS
298 def __cmp__(self, other):
299 return cmp(self._dict, other._dict)
301 def __delitem__(self, key):
302 special = self._special_del.get(key)
308 def __getitem__(self, key):
309 return self._dict[key]
311 def __setitem__(self, key, value):
312 special = self._special_set.get(key)
314 special(self, key, value)
316 if not SCons.Util.is_valid_construction_var(key):
317 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
318 self._dict[key] = value
320 def get(self, key, default=None):
321 "Emulates the get() method of dictionaries."""
322 return self._dict.get(key, default)
324 def has_key(self, key):
325 return self._dict.has_key(key)
328 return self._dict.items()
330 def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
331 if node_factory is _null:
332 node_factory = self.fs.File
333 if lookup_list is _null:
334 lookup_list = self.lookup_list
339 if SCons.Util.is_List(args):
340 args = SCons.Util.flatten(args)
346 if SCons.Util.is_String(v):
348 for l in lookup_list:
353 if SCons.Util.is_String(n):
354 # n = self.subst(n, raw=1, **kw)
356 n = apply(self.subst, (n,), kw)
359 if SCons.Util.is_List(n):
364 # v = node_factory(self.subst(v, raw=1, **kw))
366 v = node_factory(apply(self.subst, (v,), kw))
367 if SCons.Util.is_List(v):
382 def subst(self, string, raw=0, target=None, source=None, conv=None):
383 """Recursively interpolates construction variables from the
384 Environment into the specified string, returning the expanded
385 result. Construction variables are specified by a $ prefix
386 in the string and begin with an initial underscore or
387 alphabetic character followed by any number of underscores
388 or alphanumeric characters. The construction variable names
389 may be surrounded by curly braces to separate the name from
394 lvars['__env__'] = self
395 return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
397 def subst_kw(self, kw, raw=0, target=None, source=None):
399 for k, v in kw.items():
400 k = self.subst(k, raw, target, source)
401 if SCons.Util.is_String(v):
402 v = self.subst(v, raw, target, source)
406 def subst_list(self, string, raw=0, target=None, source=None, conv=None):
407 """Calls through to SCons.Subst.scons_subst_list(). See
408 the documentation for that function."""
411 lvars['__env__'] = self
412 return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
414 def subst_path(self, path, target=None, source=None):
415 """Substitute a path list, turning EntryProxies into Nodes
416 and leaving Nodes (and other objects) as-is."""
418 if not SCons.Util.is_List(path):
422 """This is the "string conversion" routine that we have our
423 substitutions use to return Nodes, not strings. This relies
424 on the fact that an EntryProxy object has a get() method that
425 returns the underlying Node that it wraps, which is a bit of
426 architectural dependence that we might need to break or modify
427 in the future in response to additional requirements."""
430 except AttributeError:
438 if SCons.Util.is_String(p):
439 p = self.subst(p, target=target, source=source, conv=s)
440 if SCons.Util.is_List(p):
444 # We have an object plus a string, or multiple
445 # objects that we need to smush together. No choice
446 # but to make them into a string.
447 p = string.join(map(SCons.Util.to_String, p), '')
453 subst_target_source = subst
455 def backtick(self, command):
457 if SCons.Util.is_List(command):
458 p = subprocess.Popen(command,
459 stdout=subprocess.PIPE,
460 stderr=subprocess.PIPE,
461 universal_newlines=True)
463 p = subprocess.Popen(command,
464 stdout=subprocess.PIPE,
465 stderr=subprocess.PIPE,
466 universal_newlines=True,
468 out = p.stdout.read()
470 err = p.stderr.read()
475 sys.stderr.write(err)
477 raise OSError("'%s' exited %d" % (command, status))
480 def AddMethod(self, function, name=None):
482 Adds the specified function as a method of this construction
483 environment with the specified name. If the name is omitted,
484 the default name is the name of the function itself.
486 SCons.Util.AddMethod(self, function, name)
488 def Override(self, overrides):
490 Produce a modified environment whose variables are overriden by
491 the overrides dictionaries. "overrides" is a dictionary that
492 will override the variables of this environment.
494 This function is much more efficient than Clone() or creating
495 a new Environment because it doesn't copy the construction
496 environment dictionary, it just wraps the underlying construction
497 environment, and doesn't even create a wrapper object if there
501 o = copy_non_reserved_keywords(overrides)
503 for key, value in o.items():
504 overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
506 env = OverrideEnvironment(self, overrides)
511 def ParseFlags(self, *flags):
513 Parse the set of flags and return a dict with the flags placed
514 in the appropriate entry. The flags are treated as a typical
515 set of command-line flags for a GNU-like toolchain and used to
516 populate the entries in the dict immediately below. If one of
517 the flag strings begins with a bang (exclamation mark), it is
518 assumed to be a command and the rest of the string is executed;
519 the result of that evaluation is then added to the dict.
522 'ASFLAGS' : SCons.Util.CLVar(''),
523 'CFLAGS' : SCons.Util.CLVar(''),
524 'CCFLAGS' : SCons.Util.CLVar(''),
526 'CPPFLAGS' : SCons.Util.CLVar(''),
528 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
529 'FRAMEWORKS' : SCons.Util.CLVar(''),
532 'LINKFLAGS' : SCons.Util.CLVar(''),
536 # The use of the "me" parameter to provide our own name for
537 # recursion is an egregious hack to support Python 2.1 and before.
538 def do_parse(arg, me, self = self, dict = dict):
539 # if arg is a sequence, recurse with each element
543 if not SCons.Util.is_String(arg):
544 for t in arg: me(t, me)
547 # if arg is a command, execute it
549 arg = self.backtick(arg[1:])
551 # utility function to deal with -D option
552 def append_define(name, dict = dict):
553 t = string.split(name, '=')
555 dict['CPPDEFINES'].append(name)
557 dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
559 # Loop through the flags and add them to the appropriate option.
560 # This tries to strike a balance between checking for all possible
561 # flags and keeping the logic to a finite size, so it doesn't
562 # check for some that don't occur often. It particular, if the
563 # flag is not known to occur in a config script and there's a way
564 # of passing the flag to the right place (by wrapping it in a -W
565 # flag, for example) we don't check for it. Note that most
566 # preprocessor options are not handled, since unhandled options
567 # are placed in CCFLAGS, so unless the preprocessor is invoked
568 # separately, these flags will still get to the preprocessor.
569 # Other options not currently handled:
570 # -iqoutedir (preprocessor search path)
571 # -u symbol (linker undefined symbol)
572 # -s (linker strip files)
573 # -static* (linker static binding)
574 # -shared* (linker dynamic binding)
575 # -symbolic (linker global binding)
576 # -R dir (deprecated linker rpath)
577 # IBM compilers may also accept -qframeworkdir=foo
579 params = string.split(arg)
580 append_next_arg_to = None # for multi-word args
582 if append_next_arg_to:
583 if append_next_arg_to == 'CPPDEFINES':
585 elif append_next_arg_to == '-include':
586 t = ('-include', self.fs.File(arg))
587 dict['CCFLAGS'].append(t)
588 elif append_next_arg_to == '-isysroot':
589 t = ('-isysroot', arg)
590 dict['CCFLAGS'].append(t)
591 dict['LINKFLAGS'].append(t)
592 elif append_next_arg_to == '-arch':
594 dict['CCFLAGS'].append(t)
595 dict['LINKFLAGS'].append(t)
597 dict[append_next_arg_to].append(arg)
598 append_next_arg_to = None
599 elif not arg[0] in ['-', '+']:
600 dict['LIBS'].append(self.fs.File(arg))
601 elif arg[:2] == '-L':
603 dict['LIBPATH'].append(arg[2:])
605 append_next_arg_to = 'LIBPATH'
606 elif arg[:2] == '-l':
608 dict['LIBS'].append(arg[2:])
610 append_next_arg_to = 'LIBS'
611 elif arg[:2] == '-I':
613 dict['CPPPATH'].append(arg[2:])
615 append_next_arg_to = 'CPPPATH'
616 elif arg[:4] == '-Wa,':
617 dict['ASFLAGS'].append(arg[4:])
618 dict['CCFLAGS'].append(arg)
619 elif arg[:4] == '-Wl,':
620 if arg[:11] == '-Wl,-rpath=':
621 dict['RPATH'].append(arg[11:])
622 elif arg[:7] == '-Wl,-R,':
623 dict['RPATH'].append(arg[7:])
624 elif arg[:6] == '-Wl,-R':
625 dict['RPATH'].append(arg[6:])
627 dict['LINKFLAGS'].append(arg)
628 elif arg[:4] == '-Wp,':
629 dict['CPPFLAGS'].append(arg)
630 elif arg[:2] == '-D':
632 append_define(arg[2:])
634 append_next_arg_to = 'CPPDEFINES'
635 elif arg == '-framework':
636 append_next_arg_to = 'FRAMEWORKS'
637 elif arg[:14] == '-frameworkdir=':
638 dict['FRAMEWORKPATH'].append(arg[14:])
639 elif arg[:2] == '-F':
641 dict['FRAMEWORKPATH'].append(arg[2:])
643 append_next_arg_to = 'FRAMEWORKPATH'
644 elif arg == '-mno-cygwin':
645 dict['CCFLAGS'].append(arg)
646 dict['LINKFLAGS'].append(arg)
647 elif arg == '-mwindows':
648 dict['LINKFLAGS'].append(arg)
649 elif arg == '-pthread':
650 dict['CCFLAGS'].append(arg)
651 dict['LINKFLAGS'].append(arg)
652 elif arg[:5] == '-std=':
653 dict['CFLAGS'].append(arg) # C only
655 dict['CCFLAGS'].append(arg)
656 dict['LINKFLAGS'].append(arg)
657 elif arg in ['-include', '-isysroot', '-arch']:
658 append_next_arg_to = arg
660 dict['CCFLAGS'].append(arg)
663 do_parse(arg, do_parse)
666 def MergeFlags(self, args, unique=1):
668 Merge the dict in args into the construction variables. If args
669 is not a dict, it is converted into a dict using ParseFlags.
670 If unique is not set, the flags are appended rather than merged.
673 if not SCons.Util.is_Dict(args):
674 args = self.ParseFlags(args)
676 apply(self.Append, (), args)
678 for key, value in args.items():
689 # Add orig and value. The logic here was lifted from
690 # part of env.Append() (see there for a lot of comments
691 # about the order in which things are tried) and is
692 # used mainly to handle coercion of strings to CLVar to
693 # "do the right thing" given (e.g.) an original CCFLAGS
694 # string variable like '-pipe -Wall'.
697 except (KeyError, TypeError):
699 add_to_orig = orig.append
700 except AttributeError:
701 value.insert(0, orig)
706 if key[-4:] == 'PATH':
707 ### keep left-most occurence
712 ### keep right-most occurence
720 class Base(SubstitutionEnvironment):
721 """Base class for "real" construction Environments. These are the
722 primary objects used to communicate dependency and construction
723 information to the build engine.
725 Keyword arguments supplied when the construction Environment
726 is created are construction variables used to initialize the
730 if SCons.Memoize.use_memoizer:
731 __metaclass__ = SCons.Memoize.Memoized_Metaclass
733 memoizer_counters = []
735 #######################################################################
736 # This is THE class for interacting with the SCons build engine,
737 # and it contains a lot of stuff, so we're going to try to keep this
738 # a little organized by grouping the methods.
739 #######################################################################
741 #######################################################################
742 # Methods that make an Environment act like a dictionary. These have
743 # the expected standard names for Python mapping objects. Note that
744 # we don't actually make an Environment a subclass of UserDict for
745 # performance reasons. Note also that we only supply methods for
746 # dictionary functionality that we actually need and use.
747 #######################################################################
756 Initialization of a basic SCons construction environment,
757 including setting up special construction variables like BUILDER,
758 PLATFORM, etc., and searching for and applying available Tools.
760 Note that we do *not* call the underlying base class
761 (SubsitutionEnvironment) initialization, because we need to
762 initialize things in a very specific order that doesn't work
763 with the much simpler base class initialization.
765 if __debug__: logInstanceCreation(self, 'Environment.Base')
767 self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS()
768 self.ans = SCons.Node.Alias.default_ans
769 self.lookup_list = SCons.Node.arg2nodes_lookups
770 self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
773 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
776 platform = self._dict.get('PLATFORM', None)
778 platform = SCons.Platform.Platform()
779 if SCons.Util.is_String(platform):
780 platform = SCons.Platform.Platform(platform)
781 self._dict['PLATFORM'] = str(platform)
784 # Apply the passed-in variables and customizable options to the
785 # environment before calling the tools, because they may use
786 # some of them during initialization.
787 apply(self.Replace, (), kw)
790 keys = keys + options.keys()
796 save[k] = self._dict[k]
798 # No value may have been set if they tried to pass in a
799 # reserved variable name like TARGETS.
803 tools = self._dict.get('TOOLS', None)
806 apply_tools(self, tools, toolpath)
808 # Now restore the passed-in variables and customized options
809 # to the environment, since the values the user set explicitly
810 # should override any values set by the tools.
811 for key, val in save.items():
812 self._dict[key] = val
814 #######################################################################
815 # Utility methods that are primarily for internal use by SCons.
816 # These begin with lower-case letters.
817 #######################################################################
819 def get_builder(self, name):
820 """Fetch the builder with the specified name from the environment.
823 return self._dict['BUILDERS'][name]
827 def get_calculator(self):
829 module = self._calc_module
830 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
831 except AttributeError:
832 # Note that we're calling get_calculator() here, so the
833 # DefaultEnvironment() must have a _calc_module attribute
834 # to avoid infinite recursion.
835 c = SCons.Defaults.DefaultEnvironment().get_calculator()
838 def get_factory(self, factory, default='File'):
839 """Return a factory function for creating Nodes for this
840 construction environment.
844 is_node = issubclass(factory, SCons.Node.Node)
846 # The specified factory isn't a Node itself--it's
847 # most likely None, or possibly a callable.
851 # The specified factory is a Node (sub)class. Try to
852 # return the FS method that corresponds to the Node's
853 # name--that is, we return self.fs.Dir if they want a Dir,
854 # self.fs.File for a File, etc.
855 try: name = factory.__name__
856 except AttributeError: pass
859 # They passed us None, or we picked up a name from a specified
860 # class, so return the FS method. (Note that we *don't*
861 # use our own self.{Dir,File} methods because that would
862 # cause env.subst() to be called twice on the file name,
863 # interfering with files that have $$ in them.)
864 factory = getattr(self.fs, name)
867 memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
871 return self._memo['_gsm']
878 scanners = self._dict['SCANNERS']
882 # Reverse the scanner list so that, if multiple scanners
883 # claim they can scan the same suffix, earlier scanners
884 # in the list will overwrite later scanners, so that
885 # the result looks like a "first match" to the user.
886 if not SCons.Util.is_List(scanners):
887 scanners = [scanners]
889 scanners = scanners[:] # copy so reverse() doesn't mod original
891 for scanner in scanners:
892 for k in scanner.get_skeys(self):
895 self._memo['_gsm'] = result
899 def get_scanner(self, skey):
900 """Find the appropriate scanner given a key (usually a file suffix).
902 return self._gsm().get(skey)
904 def scanner_map_delete(self, kw=None):
905 """Delete the cached scanner map (if we need to).
908 del self._memo['_gsm']
912 def _update(self, dict):
913 """Update an environment's values directly, bypassing the normal
914 checks that occur when users try to set items.
916 self._dict.update(dict)
918 def use_build_signature(self):
920 return self._build_signature
921 except AttributeError:
922 b = SCons.Defaults.DefaultEnvironment()._build_signature
923 self._build_signature = b
926 #######################################################################
927 # Public methods for manipulating an Environment. These begin with
928 # upper-case letters. The essential characteristic of methods in
929 # this section is that they do *not* have corresponding same-named
930 # global functions. For example, a stand-alone Append() function
931 # makes no sense, because Append() is all about appending values to
932 # an Environment's construction variables.
933 #######################################################################
935 def Append(self, **kw):
936 """Append values to existing construction variables
939 kw = copy_non_reserved_keywords(kw)
940 for key, val in kw.items():
941 # It would be easier on the eyes to write this using
942 # "continue" statements whenever we finish processing an item,
943 # but Python 1.5.2 apparently doesn't let you use "continue"
944 # within try:-except: blocks, so we have to nest our code.
946 orig = self._dict[key]
948 # No existing variable in the environment, so just set
949 # it to the new value.
950 self._dict[key] = val
953 # Check if the original looks like a dictionary.
954 # If it is, we can't just try adding the value because
955 # dictionaries don't have __add__() methods, and
956 # things like UserList will incorrectly coerce the
957 # original dict to a list (which we don't want).
958 update_dict = orig.update
959 except AttributeError:
961 # Most straightforward: just try to add them
962 # together. This will work in most cases, when the
963 # original and new values are of compatible types.
964 self._dict[key] = orig + val
965 except (KeyError, TypeError):
967 # Check if the original is a list.
968 add_to_orig = orig.append
969 except AttributeError:
970 # The original isn't a list, but the new
971 # value is (by process of elimination),
972 # so insert the original in the new value
973 # (if there's one to insert) and replace
974 # the variable with it.
977 self._dict[key] = val
979 # The original is a list, so append the new
980 # value to it (if there's a value to append).
984 # The original looks like a dictionary, so update it
985 # based on what we think the value looks like.
986 if SCons.Util.is_List(val):
992 except (AttributeError, TypeError, ValueError):
994 self.scanner_map_delete(kw)
996 def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
997 """Append path elements to the path 'name' in the 'ENV'
998 dictionary for this environment. Will only add any particular
999 path once, and will normpath and normcase all paths to help
1000 assure this. This can also handle the case where the env
1001 variable is a list instead of a string.
1005 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1006 orig = self._dict[envname][name]
1008 nv = SCons.Util.AppendPath(orig, newpath, sep)
1010 if not self._dict.has_key(envname):
1011 self._dict[envname] = {}
1013 self._dict[envname][name] = nv
1015 def AppendUnique(self, **kw):
1016 """Append values to existing construction variables
1017 in an Environment, if they're not already there.
1019 kw = copy_non_reserved_keywords(kw)
1020 for key, val in kw.items():
1021 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1022 self._dict[key] = val
1023 elif SCons.Util.is_Dict(self._dict[key]) and \
1024 SCons.Util.is_Dict(val):
1025 self._dict[key].update(val)
1026 elif SCons.Util.is_List(val):
1027 dk = self._dict[key]
1028 if not SCons.Util.is_List(dk):
1030 val = filter(lambda x, dk=dk: x not in dk, val)
1031 self._dict[key] = dk + val
1033 dk = self._dict[key]
1034 if SCons.Util.is_List(dk):
1035 # By elimination, val is not a list. Since dk is a
1036 # list, wrap val in a list first.
1038 self._dict[key] = dk + [val]
1040 self._dict[key] = self._dict[key] + val
1041 self.scanner_map_delete(kw)
1043 def Clone(self, tools=[], toolpath=None, **kw):
1044 """Return a copy of a construction Environment. The
1045 copy is like a Python "deep copy"--that is, independent
1046 copies are made recursively of each objects--except that
1047 a reference is copied when an object is not deep-copyable
1048 (like a function). There are no references to any mutable
1049 objects in the original Environment.
1051 clone = copy.copy(self)
1052 clone._dict = our_deepcopy(self._dict)
1054 cbd = clone._dict['BUILDERS']
1055 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
1061 apply_tools(clone, tools, toolpath)
1063 # Apply passed-in variables after the new tools.
1064 kw = copy_non_reserved_keywords(kw)
1066 for key, value in kw.items():
1067 new[key] = SCons.Subst.scons_subst_once(value, self, key)
1068 apply(clone.Replace, (), new)
1069 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
1072 def Copy(self, *args, **kw):
1073 return apply(self.Clone, args, kw)
1075 def Detect(self, progs):
1076 """Return the first available program in progs.
1078 if not SCons.Util.is_List(progs):
1081 path = self.WhereIs(prog)
1082 if path: return prog
1085 def Dictionary(self, *args):
1088 dlist = map(lambda x, s=self: s._dict[x], args)
1093 def Dump(self, key = None):
1095 Using the standard Python pretty printer, dump the contents of the
1096 scons build environment to stdout.
1098 If the key passed in is anything other than None, then that will
1099 be used as an index into the build environment dictionary and
1100 whatever is found there will be fed into the pretty printer. Note
1101 that this key is case sensitive.
1104 pp = pprint.PrettyPrinter(indent=2)
1106 dict = self.Dictionary(key)
1108 dict = self.Dictionary()
1109 return pp.pformat(dict)
1111 def FindIxes(self, paths, prefix, suffix):
1113 Search a list of paths for something that matches the prefix and suffix.
1115 paths - the list of paths or nodes.
1116 prefix - construction variable for the prefix.
1117 suffix - construction variable for the suffix.
1120 suffix = self.subst('$'+suffix)
1121 prefix = self.subst('$'+prefix)
1124 dir,name = os.path.split(str(path))
1125 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
1128 def ParseConfig(self, command, function=None, unique=1):
1130 Use the specified function to parse the output of the command
1131 in order to modify the current environment. The 'command' can
1132 be a string or a list of strings representing a command and
1133 its arguments. 'Function' is an optional argument that takes
1134 the environment, the output of the command, and the unique flag.
1135 If no function is specified, MergeFlags, which treats the output
1136 as the result of a typical 'X-config' command (i.e. gtk-config),
1137 will merge the output into the appropriate variables.
1139 if function is None:
1140 def parse_conf(env, cmd, unique=unique):
1141 return env.MergeFlags(cmd, unique)
1142 function = parse_conf
1143 if SCons.Util.is_List(command):
1144 command = string.join(command)
1145 command = self.subst(command)
1146 return function(self, self.backtick(command))
1148 def ParseDepends(self, filename, must_exist=None, only_one=0):
1150 Parse a mkdep-style file for explicit dependencies. This is
1151 completely abusable, and should be unnecessary in the "normal"
1152 case of proper SCons configuration, but it may help make
1153 the transition from a Make hierarchy easier for some people
1154 to swallow. It can also be genuinely useful when using a tool
1155 that can write a .d file, but for which writing a scanner would
1158 filename = self.subst(filename)
1160 fp = open(filename, 'r')
1165 lines = SCons.Util.LogicalLines(fp).readlines()
1166 lines = filter(lambda l: l[0] != '#', lines)
1170 target, depends = string.split(line, ':', 1)
1171 except (AttributeError, TypeError, ValueError):
1172 # Python 1.5.2 throws TypeError if line isn't a string,
1173 # Python 2.x throws AttributeError because it tries
1174 # to call line.split(). Either can throw ValueError
1175 # if the line doesn't split into two or more elements.
1178 tdlist.append((string.split(target), string.split(depends)))
1180 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
1181 if len(targets) > 1:
1182 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
1183 for target, depends in tdlist:
1184 self.Depends(target, depends)
1186 def Platform(self, platform):
1187 platform = self.subst(platform)
1188 return SCons.Platform.Platform(platform)(self)
1190 def Prepend(self, **kw):
1191 """Prepend values to existing construction variables
1194 kw = copy_non_reserved_keywords(kw)
1195 for key, val in kw.items():
1196 # It would be easier on the eyes to write this using
1197 # "continue" statements whenever we finish processing an item,
1198 # but Python 1.5.2 apparently doesn't let you use "continue"
1199 # within try:-except: blocks, so we have to nest our code.
1201 orig = self._dict[key]
1203 # No existing variable in the environment, so just set
1204 # it to the new value.
1205 self._dict[key] = val
1208 # Check if the original looks like a dictionary.
1209 # If it is, we can't just try adding the value because
1210 # dictionaries don't have __add__() methods, and
1211 # things like UserList will incorrectly coerce the
1212 # original dict to a list (which we don't want).
1213 update_dict = orig.update
1214 except AttributeError:
1216 # Most straightforward: just try to add them
1217 # together. This will work in most cases, when the
1218 # original and new values are of compatible types.
1219 self._dict[key] = val + orig
1220 except (KeyError, TypeError):
1222 # Check if the added value is a list.
1223 add_to_val = val.append
1224 except AttributeError:
1225 # The added value isn't a list, but the
1226 # original is (by process of elimination),
1227 # so insert the the new value in the original
1228 # (if there's one to insert).
1232 # The added value is a list, so append
1233 # the original to it (if there's a value
1237 self._dict[key] = val
1239 # The original looks like a dictionary, so update it
1240 # based on what we think the value looks like.
1241 if SCons.Util.is_List(val):
1247 except (AttributeError, TypeError, ValueError):
1249 self.scanner_map_delete(kw)
1251 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
1252 """Prepend path elements to the path 'name' in the 'ENV'
1253 dictionary for this environment. Will only add any particular
1254 path once, and will normpath and normcase all paths to help
1255 assure this. This can also handle the case where the env
1256 variable is a list instead of a string.
1260 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1261 orig = self._dict[envname][name]
1263 nv = SCons.Util.PrependPath(orig, newpath, sep)
1265 if not self._dict.has_key(envname):
1266 self._dict[envname] = {}
1268 self._dict[envname][name] = nv
1270 def PrependUnique(self, **kw):
1271 """Append values to existing construction variables
1272 in an Environment, if they're not already there.
1274 kw = copy_non_reserved_keywords(kw)
1275 for key, val in kw.items():
1276 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1277 self._dict[key] = val
1278 elif SCons.Util.is_Dict(self._dict[key]) and \
1279 SCons.Util.is_Dict(val):
1280 self._dict[key].update(val)
1281 elif SCons.Util.is_List(val):
1282 dk = self._dict[key]
1283 if not SCons.Util.is_List(dk):
1285 val = filter(lambda x, dk=dk: x not in dk, val)
1286 self._dict[key] = val + dk
1288 dk = self._dict[key]
1289 if SCons.Util.is_List(dk):
1290 # By elimination, val is not a list. Since dk is a
1291 # list, wrap val in a list first.
1293 self._dict[key] = [val] + dk
1295 self._dict[key] = val + dk
1296 self.scanner_map_delete(kw)
1298 def Replace(self, **kw):
1299 """Replace existing construction variables in an Environment
1300 with new construction variables and/or values.
1303 kwbd = our_deepcopy(kw['BUILDERS'])
1305 self.__setitem__('BUILDERS', kwbd)
1308 kw = copy_non_reserved_keywords(kw)
1309 self._update(our_deepcopy(kw))
1310 self.scanner_map_delete(kw)
1312 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1314 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1316 env - Environment used to interpolate variables.
1317 path - the path that will be modified.
1318 old_prefix - construction variable for the old prefix.
1319 old_suffix - construction variable for the old suffix.
1320 new_prefix - construction variable for the new prefix.
1321 new_suffix - construction variable for the new suffix.
1323 old_prefix = self.subst('$'+old_prefix)
1324 old_suffix = self.subst('$'+old_suffix)
1326 new_prefix = self.subst('$'+new_prefix)
1327 new_suffix = self.subst('$'+new_suffix)
1329 dir,name = os.path.split(str(path))
1330 if name[:len(old_prefix)] == old_prefix:
1331 name = name[len(old_prefix):]
1332 if name[-len(old_suffix):] == old_suffix:
1333 name = name[:-len(old_suffix)]
1334 return os.path.join(dir, new_prefix+name+new_suffix)
1336 def SetDefault(self, **kw):
1338 if self._dict.has_key(k):
1340 apply(self.Replace, (), kw)
1342 def _find_toolpath_dir(self, tp):
1343 return self.fs.Dir(self.subst(tp)).srcnode().abspath
1345 def Tool(self, tool, toolpath=None, **kw):
1346 if SCons.Util.is_String(tool):
1347 tool = self.subst(tool)
1348 if toolpath is None:
1349 toolpath = self.get('toolpath', [])
1350 toolpath = map(self._find_toolpath_dir, toolpath)
1351 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
1354 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1355 """Find prog in the path.
1359 path = self['ENV']['PATH']
1362 elif SCons.Util.is_String(path):
1363 path = self.subst(path)
1366 pathext = self['ENV']['PATHEXT']
1369 elif SCons.Util.is_String(pathext):
1370 pathext = self.subst(pathext)
1371 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1372 if path: return path
1375 #######################################################################
1376 # Public methods for doing real "SCons stuff" (manipulating
1377 # dependencies, setting attributes on targets, etc.). These begin
1378 # with upper-case letters. The essential characteristic of methods
1379 # in this section is that they all *should* have corresponding
1380 # same-named global functions.
1381 #######################################################################
1383 def Action(self, *args, **kw):
1384 def subst_string(a, self=self):
1385 if SCons.Util.is_String(a):
1388 nargs = map(subst_string, args)
1389 nkw = self.subst_kw(kw)
1390 return apply(SCons.Action.Action, nargs, nkw)
1392 def AddPreAction(self, files, action):
1393 nodes = self.arg2nodes(files, self.fs.Entry)
1394 action = SCons.Action.Action(action)
1396 for executor in map(lambda n: n.get_executor(), nodes):
1398 for executor in uniq.keys():
1399 executor.add_pre_action(action)
1402 def AddPostAction(self, files, action):
1403 nodes = self.arg2nodes(files, self.fs.Entry)
1404 action = SCons.Action.Action(action)
1406 for executor in map(lambda n: n.get_executor(), nodes):
1408 for executor in uniq.keys():
1409 executor.add_post_action(action)
1412 def Alias(self, target, source=[], action=None, **kw):
1413 tlist = self.arg2nodes(target, self.ans.Alias)
1414 if not SCons.Util.is_List(source):
1416 source = filter(None, source)
1420 # There are no source files and no action, so just
1421 # return a target list of classic Alias Nodes, without
1422 # any builder. The externally visible effect is that
1423 # this will make the wrapping Script.BuildTask class
1424 # say that there's "Nothing to be done" for this Alias,
1425 # instead of that it's "up to date."
1428 # No action, but there are sources. Re-call all the target
1429 # builders to add the sources to each target.
1432 bld = t.get_builder(AliasBuilder)
1433 result.extend(bld(self, t, source))
1436 nkw = self.subst_kw(kw)
1438 'action' : SCons.Action.Action(action),
1439 'source_factory' : self.fs.Entry,
1441 'is_explicit' : None,
1443 bld = apply(SCons.Builder.Builder, (), nkw)
1445 # Apply the Builder separately to each target so that the Aliases
1446 # stay separate. If we did one "normal" Builder call with the
1447 # whole target list, then all of the target Aliases would be
1448 # associated under a single Executor.
1451 # Calling the convert() method will cause a new Executor to be
1452 # created from scratch, so we have to explicitly initialize
1453 # it with the target's existing sources, plus our new ones,
1454 # so nothing gets lost.
1456 if b is None or b is AliasBuilder:
1459 nkw['action'] = b.action + action
1460 b = apply(SCons.Builder.Builder, (), nkw)
1462 result.extend(b(self, t, t.sources + source))
1465 def AlwaysBuild(self, *targets):
1468 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1470 t.set_always_build()
1473 def BuildDir(self, build_dir, src_dir, duplicate=1):
1474 build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
1475 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1476 self.fs.BuildDir(build_dir, src_dir, duplicate)
1478 def Builder(self, **kw):
1479 nkw = self.subst_kw(kw)
1480 return apply(SCons.Builder.Builder, [], nkw)
1482 def CacheDir(self, path):
1483 self.fs.CacheDir(self.subst(path))
1485 def Clean(self, targets, files):
1487 tlist = self.arg2nodes(targets, self.fs.Entry)
1488 flist = self.arg2nodes(files, self.fs.Entry)
1491 CleanTargets[t].extend(flist)
1493 CleanTargets[t] = flist
1495 def Configure(self, *args, **kw):
1498 nargs = nargs + self.subst_list(args)[0]
1499 nkw = self.subst_kw(kw)
1500 nkw['_depth'] = kw.get('_depth', 0) + 1
1502 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1505 return apply(SCons.SConf.SConf, nargs, nkw)
1507 def Command(self, target, source, action, **kw):
1508 """Builds the supplied target files from the supplied
1509 source files using the supplied action. Action may
1510 be any type that the Builder constructor will accept
1514 'target_factory' : self.fs.Entry,
1515 'source_factory' : self.fs.Entry,
1517 try: bkw['source_scanner'] = kw['source_scanner']
1518 except KeyError: pass
1519 else: del kw['source_scanner']
1520 bld = apply(SCons.Builder.Builder, (), bkw)
1521 return apply(bld, (self, target, source), kw)
1523 def Depends(self, target, dependency):
1524 """Explicity specify that 'target's depend on 'dependency'."""
1525 tlist = self.arg2nodes(target, self.fs.Entry)
1526 dlist = self.arg2nodes(dependency, self.fs.Entry)
1528 t.add_dependency(dlist)
1531 def Dir(self, name, *args, **kw):
1534 return apply(self.fs.Dir, (self.subst(name),) + args, kw)
1536 def NoClean(self, *targets):
1537 """Tags a target so that it will not be cleaned by -c"""
1540 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1545 def NoCache(self, *targets):
1546 """Tags a target so that it will not be cached"""
1549 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1554 def Entry(self, name, *args, **kw):
1557 return apply(self.fs.Entry, (self.subst(name),) + args, kw)
1559 def Environment(self, **kw):
1560 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1562 def Execute(self, action, *args, **kw):
1563 """Directly execute an action through an Environment
1565 action = apply(self.Action, (action,) + args, kw)
1566 return action([], [], self)
1568 def File(self, name, *args, **kw):
1571 return apply(self.fs.File, (self.subst(name),) + args, kw)
1573 def FindFile(self, file, dirs):
1574 file = self.subst(file)
1575 nodes = self.arg2nodes(dirs, self.fs.Dir)
1576 return SCons.Node.FS.find_file(file, tuple(nodes))
1578 def Flatten(self, sequence):
1579 return SCons.Util.flatten(sequence)
1581 def GetBuildPath(self, files):
1582 result = map(str, self.arg2nodes(files, self.fs.Entry))
1583 if SCons.Util.is_List(files):
1588 def Ignore(self, target, dependency):
1589 """Ignore a dependency."""
1590 tlist = self.arg2nodes(target, self.fs.Entry)
1591 dlist = self.arg2nodes(dependency, self.fs.Entry)
1596 def Install(self, dir, source):
1597 """Install specified files in the given directory."""
1599 dnodes = self.arg2nodes(dir, self.fs.Dir)
1601 fmt = "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?"
1602 raise SCons.Errors.UserError, fmt % str(dir)
1604 sources = self.arg2nodes(source, self.fs.Entry)
1606 if SCons.Util.is_List(source):
1607 s = repr(map(str, source))
1610 fmt = "Source `%s' of Install() is neither a file nor a directory. Install() source must be one or more files or directories"
1611 raise SCons.Errors.UserError, fmt % s
1613 for dnode in dnodes:
1615 # Prepend './' so the lookup doesn't interpret an initial
1616 # '#' on the file name portion as meaning the Node should
1617 # be relative to the top-level SConstruct directory.
1618 target = dnode.Entry('.'+os.sep+src.name)
1619 tgt.extend(InstallBuilder(self, target, src))
1622 def InstallAs(self, target, source):
1623 """Install sources as targets."""
1624 sources = self.arg2nodes(source, self.fs.Entry)
1625 targets = self.arg2nodes(target, self.fs.Entry)
1626 if len(sources) != len(targets):
1627 if not SCons.Util.is_List(target):
1629 if not SCons.Util.is_List(source):
1631 t = repr(map(str, target))
1632 s = repr(map(str, source))
1633 fmt = "Target (%s) and source (%s) lists of InstallAs() must be the same length."
1634 raise SCons.Errors.UserError, fmt % (t, s)
1636 for src, tgt in map(lambda x, y: (x, y), sources, targets):
1637 result.extend(InstallBuilder(self, tgt, src))
1640 def Literal(self, string):
1641 return SCons.Subst.Literal(string)
1643 def Local(self, *targets):
1645 for targ in targets:
1646 if isinstance(targ, SCons.Node.Node):
1650 for t in self.arg2nodes(targ, self.fs.Entry):
1655 def Precious(self, *targets):
1658 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1663 def Repository(self, *dirs, **kw):
1664 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1665 apply(self.fs.Repository, dirs, kw)
1667 def Scanner(self, *args, **kw):
1670 if SCons.Util.is_String(arg):
1671 arg = self.subst(arg)
1673 nkw = self.subst_kw(kw)
1674 return apply(SCons.Scanner.Base, nargs, nkw)
1676 def SConsignFile(self, name=".sconsign", dbm_module=None):
1677 if not name is None:
1678 name = self.subst(name)
1679 if not os.path.isabs(name):
1680 name = os.path.join(str(self.fs.SConstruct_dir), name)
1681 SCons.SConsign.File(name, dbm_module)
1683 def SideEffect(self, side_effect, target):
1684 """Tell scons that side_effects are built as side
1685 effects of building targets."""
1686 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1687 targets = self.arg2nodes(target, self.fs.Entry)
1689 for side_effect in side_effects:
1690 if side_effect.multiple_side_effect_has_builder():
1691 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1692 side_effect.add_source(targets)
1693 side_effect.side_effect = 1
1694 self.Precious(side_effect)
1695 for target in targets:
1696 target.side_effects.append(side_effect)
1699 def SourceCode(self, entry, builder):
1700 """Arrange for a source code builder for (part of) a tree."""
1701 entries = self.arg2nodes(entry, self.fs.Entry)
1702 for entry in entries:
1703 entry.set_src_builder(builder)
1706 def SourceSignatures(self, type):
1707 type = self.subst(type)
1710 import SCons.Sig.MD5
1712 msg = "No MD5 module available, using time stamps"
1713 SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg)
1714 import SCons.Sig.TimeStamp
1715 self._calc_module = SCons.Sig.TimeStamp
1717 self._calc_module = SCons.Sig.MD5
1718 elif type == 'timestamp':
1719 import SCons.Sig.TimeStamp
1720 self._calc_module = SCons.Sig.TimeStamp
1722 raise UserError, "Unknown source signature type '%s'"%type
1724 def Split(self, arg):
1725 """This function converts a string or list into a list of strings
1726 or Nodes. This makes things easier for users by allowing files to
1727 be specified as a white-space separated list to be split.
1728 The input rules are:
1729 - A single string containing names separated by spaces. These will be
1730 split apart at the spaces.
1731 - A single Node instance
1732 - A list containing either strings or Node instances. Any strings
1733 in the list are not split at spaces.
1734 In all cases, the function returns a list of Nodes and strings."""
1735 if SCons.Util.is_List(arg):
1736 return map(self.subst, arg)
1737 elif SCons.Util.is_String(arg):
1738 return string.split(self.subst(arg))
1740 return [self.subst(arg)]
1742 def TargetSignatures(self, type):
1743 type = self.subst(type)
1745 self._build_signature = 1
1746 elif type == 'content':
1747 self._build_signature = 0
1749 raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1751 def Value(self, value, built_value=None):
1754 return SCons.Node.Python.Value(value, built_value)
1756 class OverrideEnvironment(Base):
1757 """A proxy that overrides variables in a wrapped construction
1758 environment by returning values from an overrides dictionary in
1759 preference to values from the underlying subject environment.
1761 This is a lightweight (I hope) proxy that passes through most use of
1762 attributes to the underlying Environment.Base class, but has just
1763 enough additional methods defined to act like a real construction
1764 environment with overridden values. It can wrap either a Base
1765 construction environment, or another OverrideEnvironment, which
1766 can in turn nest arbitrary OverrideEnvironments...
1768 Note that we do *not* call the underlying base class
1769 (SubsitutionEnvironment) initialization, because we get most of those
1770 from proxying the attributes of the subject construction environment.
1771 But because we subclass SubstitutionEnvironment, this class also
1772 has inherited arg2nodes() and subst*() methods; those methods can't
1773 be proxied because they need *this* object's methods to fetch the
1774 values from the overrides dictionary.
1777 if SCons.Memoize.use_memoizer:
1778 __metaclass__ = SCons.Memoize.Memoized_Metaclass
1780 def __init__(self, subject, overrides={}):
1781 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
1782 self.__dict__['__subject'] = subject
1783 self.__dict__['overrides'] = overrides
1785 # Methods that make this class act like a proxy.
1786 def __getattr__(self, name):
1787 return getattr(self.__dict__['__subject'], name)
1788 def __setattr__(self, name, value):
1789 setattr(self.__dict__['__subject'], name, value)
1791 # Methods that make this class act like a dictionary.
1792 def __getitem__(self, key):
1794 return self.__dict__['overrides'][key]
1796 return self.__dict__['__subject'].__getitem__(key)
1797 def __setitem__(self, key, value):
1798 if not SCons.Util.is_valid_construction_var(key):
1799 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
1800 self.__dict__['overrides'][key] = value
1801 def __delitem__(self, key):
1803 del self.__dict__['overrides'][key]
1809 result = self.__dict__['__subject'].__delitem__(key)
1815 def get(self, key, default=None):
1816 """Emulates the get() method of dictionaries."""
1818 return self.__dict__['overrides'][key]
1820 return self.__dict__['__subject'].get(key, default)
1821 def has_key(self, key):
1823 self.__dict__['overrides'][key]
1826 return self.__dict__['__subject'].has_key(key)
1827 def Dictionary(self):
1828 """Emulates the items() method of dictionaries."""
1829 d = self.__dict__['__subject'].Dictionary().copy()
1830 d.update(self.__dict__['overrides'])
1833 """Emulates the items() method of dictionaries."""
1834 return self.Dictionary().items()
1836 # Overridden private construction environment methods.
1837 def _update(self, dict):
1838 """Update an environment's values directly, bypassing the normal
1839 checks that occur when users try to set items.
1841 self.__dict__['overrides'].update(dict)
1844 return self.__dict__['__subject'].gvars()
1847 lvars = self.__dict__['__subject'].lvars()
1848 lvars.update(self.__dict__['overrides'])
1851 # Overridden public construction environment methods.
1852 def Replace(self, **kw):
1853 kw = copy_non_reserved_keywords(kw)
1854 self.__dict__['overrides'].update(our_deepcopy(kw))
1856 # The entry point that will be used by the external world
1857 # to refer to a construction environment. This allows the wrapper
1858 # interface to extend a construction environment for its own purposes
1859 # by subclassing SCons.Environment.Base and then assigning the
1860 # class to SCons.Environment.Environment.
1864 # An entry point for returning a proxy subclass instance that overrides
1865 # the subst*() methods so they don't actually perform construction
1866 # variable substitution. This is specifically intended to be the shim
1867 # layer in between global function calls (which don't want construction
1868 # variable substitution) and the DefaultEnvironment() (which would
1869 # substitute variables if left to its own devices)."""
1871 # We have to wrap this in a function that allows us to delay definition of
1872 # the class until it's necessary, so that when it subclasses Environment
1873 # it will pick up whatever Environment subclass the wrapper interface
1874 # might have assigned to SCons.Environment.Environment.
1876 def NoSubstitutionProxy(subject):
1877 class _NoSubstitutionProxy(Environment):
1878 def __init__(self, subject):
1879 self.__dict__['__subject'] = subject
1880 def __getattr__(self, name):
1881 return getattr(self.__dict__['__subject'], name)
1882 def __setattr__(self, name, value):
1883 return setattr(self.__dict__['__subject'], name, value)
1884 def raw_to_mode(self, dict):
1892 def subst(self, string, *args, **kwargs):
1894 def subst_kw(self, kw, *args, **kwargs):
1896 def subst_list(self, string, *args, **kwargs):
1897 nargs = (string, self,) + args
1900 self.raw_to_mode(nkw)
1901 return apply(SCons.Subst.scons_subst_list, nargs, nkw)
1902 def subst_target_source(self, string, *args, **kwargs):
1903 nargs = (string, self,) + args
1906 self.raw_to_mode(nkw)
1907 return apply(SCons.Subst.scons_subst, nargs, nkw)
1908 return _NoSubstitutionProxy(subject)