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__"
44 from UserDict import UserDict
48 from SCons.Debug import logInstanceCreation
53 import SCons.Node.Alias
55 import SCons.Node.Python
68 _warn_copy_deprecated = True
69 _warn_source_signatures_deprecated = True
70 _warn_target_signatures_deprecated = True
75 semi_deepcopy = SCons.Util.semi_deepcopy
77 # Pull UserError into the global name space for the benefit of
78 # Environment().SourceSignatures(), which has some import statements
79 # which seem to mess up its ability to reference SCons directly.
80 UserError = SCons.Errors.UserError
82 def alias_builder(env, target, source):
85 AliasBuilder = SCons.Builder.Builder(action = alias_builder,
86 target_factory = SCons.Node.Alias.default_ans.Alias,
87 source_factory = SCons.Node.FS.Entry,
92 def apply_tools(env, tools, toolpath):
93 # Store the toolpath in the Environment.
94 if toolpath is not None:
95 env['toolpath'] = toolpath
99 # Filter out null tools from the list.
100 for tool in filter(None, tools):
101 if SCons.Util.is_List(tool) or type(tool)==type(()):
103 toolargs = tool[1] # should be a dict of kw args
104 tool = apply(env.Tool, [toolname], toolargs)
108 # These names are controlled by SCons; users should never set or override
109 # them. This warning can optionally be turned off, but scons will still
110 # ignore the illegal variable names even if it's off.
111 reserved_construction_var_names = \
112 ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
114 def copy_non_reserved_keywords(dict):
115 result = semi_deepcopy(dict)
116 for k in result.keys():
117 if k in reserved_construction_var_names:
118 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
119 "Ignoring attempt to set reserved variable `%s'" % k)
123 def _set_reserved(env, key, value):
124 msg = "Ignoring attempt to set reserved variable `%s'" % key
125 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg)
127 def _set_BUILDERS(env, key, value):
133 bd = BuilderDict(kwbd, env)
137 def _del_SCANNERS(env, key):
139 env.scanner_map_delete()
141 def _set_SCANNERS(env, key, value):
142 env._dict[key] = value
143 env.scanner_map_delete()
147 # The following is partly based on code in a comment added by Peter
148 # Shannon at the following page (there called the "transplant" class):
150 # ASPN : Python Cookbook : Dynamically added methods to a class
151 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
153 # We had independently been using the idiom as BuilderWrapper, but
154 # factoring out the common parts into this base class, and making
155 # BuilderWrapper a subclass that overrides __call__() to enforce specific
156 # Builder calling conventions, simplified some of our higher-layer code.
160 A generic Wrapper class that associates a method (which can
161 actually be any callable) with an object. As part of creating this
162 MethodWrapper object an attribute with the specified (by default,
163 the name of the supplied method) is added to the underlying object.
164 When that new "method" is called, our __call__() method adds the
165 object as the first argument, simulating the Python behavior of
166 supplying "self" on method calls.
168 We hang on to the name by which the method was added to the underlying
169 base class so that we can provide a method to "clone" ourselves onto
170 a new underlying object being copied (without which we wouldn't need
173 def __init__(self, object, method, name=None):
175 name = method.__name__
179 setattr(self.object, name, self)
181 def __call__(self, *args, **kwargs):
182 nargs = (self.object,) + args
183 return apply(self.method, nargs, kwargs)
185 def clone(self, new_object):
187 Returns an object that re-binds the underlying "method" to
188 the specified new object.
190 return self.__class__(new_object, self.method, self.name)
192 class BuilderWrapper(MethodWrapper):
194 A MethodWrapper subclass that that associates an environment with
197 This mainly exists to wrap the __call__() function so that all calls
198 to Builders can have their argument lists massaged in the same way
199 (treat a lone argument as the source, treat two arguments as target
200 then source, make sure both target and source are lists) without
201 having to have cut-and-paste code to do it.
203 As a bit of obsessive backwards compatibility, we also intercept
204 attempts to get or set the "env" or "builder" attributes, which were
205 the names we used before we put the common functionality into the
206 MethodWrapper base class. We'll keep this around for a while in case
207 people shipped Tool modules that reached into the wrapper (like the
208 Tool/qt.py module does, or did). There shouldn't be a lot attribute
209 fetching or setting on these, so a little extra work shouldn't hurt.
211 def __call__(self, target=None, source=_null, *args, **kw):
215 if not target is None and not SCons.Util.is_List(target):
217 if not source is None and not SCons.Util.is_List(source):
219 return apply(MethodWrapper.__call__, (self, target, source) + args, kw)
222 return '<BuilderWrapper %s>' % repr(self.name)
225 return self.__repr__()
227 def __getattr__(self, name):
230 elif name == 'builder':
233 return self.__dict__[name]
235 def __setattr__(self, name, value):
238 elif name == 'builder':
241 self.__dict__[name] = value
243 # This allows a Builder to be executed directly
244 # through the Environment to which it's attached.
245 # In practice, we shouldn't need this, because
246 # builders actually get executed through a Node.
247 # But we do have a unit test for this, and can't
248 # yet rule out that it would be useful in the
249 # future, so leave it for now.
250 #def execute(self, **kw):
251 # kw['env'] = self.env
252 # apply(self.builder.execute, (), kw)
254 class BuilderDict(UserDict):
255 """This is a dictionary-like class used by an Environment to hold
256 the Builders. We need to do this because every time someone changes
257 the Builders in the Environment's BUILDERS dictionary, we must
258 update the Environment's attributes."""
259 def __init__(self, dict, env):
260 # Set self.env before calling the superclass initialization,
261 # because it will end up calling our other methods, which will
262 # need to point the values in this dictionary to self.env.
264 UserDict.__init__(self, dict)
266 def __semi_deepcopy__(self):
267 return self.__class__(self.data, self.env)
269 def __setitem__(self, item, val):
271 method = getattr(self.env, item).method
272 except AttributeError:
275 self.env.RemoveMethod(method)
276 UserDict.__setitem__(self, item, val)
277 BuilderWrapper(self.env, val, item)
279 def __delitem__(self, item):
280 UserDict.__delitem__(self, item)
281 delattr(self.env, item)
283 def update(self, dict):
284 for i, v in dict.items():
285 self.__setitem__(i, v)
289 _is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
291 def is_valid_construction_var(varstr):
292 """Return if the specified string is a legitimate construction
295 return _is_valid_var.match(varstr)
299 class SubstitutionEnvironment:
300 """Base class for different flavors of construction environments.
302 This class contains a minimal set of methods that handle contruction
303 variable expansion and conversion of strings to Nodes, which may or
304 may not be actually useful as a stand-alone class. Which methods
305 ended up in this class is pretty arbitrary right now. They're
306 basically the ones which we've empirically determined are common to
307 the different construction environment subclasses, and most of the
308 others that use or touch the underlying dictionary of construction
311 Eventually, this class should contain all the methods that we
312 determine are necessary for a "minimal" interface to the build engine.
313 A full "native Python" SCons environment has gotten pretty heavyweight
314 with all of the methods and Tools and construction variables we've
315 jammed in there, so it would be nice to have a lighter weight
316 alternative for interfaces that don't need all of the bells and
317 whistles. (At some point, we'll also probably rename this class
318 "Base," since that more reflects what we want this class to become,
319 but because we've released comments that tell people to subclass
320 Environment.Base to create their own flavors of construction
321 environment, we'll save that for a future refactoring when this
322 class actually becomes useful.)
325 if SCons.Memoize.use_memoizer:
326 __metaclass__ = SCons.Memoize.Memoized_Metaclass
328 def __init__(self, **kw):
329 """Initialization of an underlying SubstitutionEnvironment class.
331 if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
332 self.fs = SCons.Node.FS.get_default_fs()
333 self.ans = SCons.Node.Alias.default_ans
334 self.lookup_list = SCons.Node.arg2nodes_lookups
335 self._dict = kw.copy()
337 self.added_methods = []
340 def _init_special(self):
341 """Initial the dispatch tables for special handling of
342 special construction variables."""
343 self._special_del = {}
344 self._special_del['SCANNERS'] = _del_SCANNERS
346 self._special_set = {}
347 for key in reserved_construction_var_names:
348 self._special_set[key] = _set_reserved
349 self._special_set['BUILDERS'] = _set_BUILDERS
350 self._special_set['SCANNERS'] = _set_SCANNERS
352 # Freeze the keys of self._special_set in a list for use by
353 # methods that need to check. (Empirically, list scanning has
354 # gotten better than dict.has_key() in Python 2.5.)
355 self._special_set_keys = self._special_set.keys()
357 def __cmp__(self, other):
358 return cmp(self._dict, other._dict)
360 def __delitem__(self, key):
361 special = self._special_del.get(key)
367 def __getitem__(self, key):
368 return self._dict[key]
370 def __setitem__(self, key, value):
371 # This is heavily used. This implementation is the best we have
372 # according to the timings in bench/env.__setitem__.py.
374 # The "key in self._special_set_keys" test here seems to perform
375 # pretty well for the number of keys we have. A hard-coded
376 # list works a little better in Python 2.5, but that has the
377 # disadvantage of maybe getting out of sync if we ever add more
378 # variable names. Using self._special_set.has_key() works a
379 # little better in Python 2.4, but is worse then this test.
380 # So right now it seems like a good trade-off, but feel free to
381 # revisit this with bench/env.__setitem__.py as needed (and
382 # as newer versions of Python come out).
383 if key in self._special_set_keys:
384 self._special_set[key](self, key, value)
386 # If we already have the entry, then it's obviously a valid
387 # key and we don't need to check. If we do check, using a
388 # global, pre-compiled regular expression directly is more
389 # efficient than calling another function or a method.
390 if not self._dict.has_key(key) \
391 and not _is_valid_var.match(key):
392 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
393 self._dict[key] = value
395 def get(self, key, default=None):
396 "Emulates the get() method of dictionaries."""
397 return self._dict.get(key, default)
399 def has_key(self, key):
400 return self._dict.has_key(key)
403 return self._dict.items()
405 def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
406 if node_factory is _null:
407 node_factory = self.fs.File
408 if lookup_list is _null:
409 lookup_list = self.lookup_list
414 args = SCons.Util.flatten(args)
418 if SCons.Util.is_String(v):
420 for l in lookup_list:
425 if SCons.Util.is_String(n):
426 # n = self.subst(n, raw=1, **kw)
428 n = apply(self.subst, (n,), kw)
431 if SCons.Util.is_List(n):
436 # v = node_factory(self.subst(v, raw=1, **kw))
438 v = node_factory(apply(self.subst, (v,), kw))
439 if SCons.Util.is_List(v):
454 def subst(self, string, raw=0, target=None, source=None, conv=None):
455 """Recursively interpolates construction variables from the
456 Environment into the specified string, returning the expanded
457 result. Construction variables are specified by a $ prefix
458 in the string and begin with an initial underscore or
459 alphabetic character followed by any number of underscores
460 or alphanumeric characters. The construction variable names
461 may be surrounded by curly braces to separate the name from
466 lvars['__env__'] = self
467 return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
469 def subst_kw(self, kw, raw=0, target=None, source=None):
471 for k, v in kw.items():
472 k = self.subst(k, raw, target, source)
473 if SCons.Util.is_String(v):
474 v = self.subst(v, raw, target, source)
478 def subst_list(self, string, raw=0, target=None, source=None, conv=None):
479 """Calls through to SCons.Subst.scons_subst_list(). See
480 the documentation for that function."""
483 lvars['__env__'] = self
484 return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
486 def subst_path(self, path, target=None, source=None):
487 """Substitute a path list, turning EntryProxies into Nodes
488 and leaving Nodes (and other objects) as-is."""
490 if not SCons.Util.is_List(path):
494 """This is the "string conversion" routine that we have our
495 substitutions use to return Nodes, not strings. This relies
496 on the fact that an EntryProxy object has a get() method that
497 returns the underlying Node that it wraps, which is a bit of
498 architectural dependence that we might need to break or modify
499 in the future in response to additional requirements."""
502 except AttributeError:
503 obj = SCons.Util.to_String_for_subst(obj)
510 if SCons.Util.is_String(p):
511 p = self.subst(p, target=target, source=source, conv=s)
512 if SCons.Util.is_List(p):
516 # We have an object plus a string, or multiple
517 # objects that we need to smush together. No choice
518 # but to make them into a string.
519 p = string.join(map(SCons.Util.to_String_for_subst, p), '')
525 subst_target_source = subst
527 def backtick(self, command):
529 if SCons.Util.is_List(command):
530 p = subprocess.Popen(command,
531 stdout=subprocess.PIPE,
532 stderr=subprocess.PIPE,
533 universal_newlines=True)
535 p = subprocess.Popen(command,
536 stdout=subprocess.PIPE,
537 stderr=subprocess.PIPE,
538 universal_newlines=True,
540 out = p.stdout.read()
542 err = p.stderr.read()
547 sys.stderr.write(err)
549 raise OSError("'%s' exited %d" % (command, status))
552 def AddMethod(self, function, name=None):
554 Adds the specified function as a method of this construction
555 environment with the specified name. If the name is omitted,
556 the default name is the name of the function itself.
558 method = MethodWrapper(self, function, name)
559 self.added_methods.append(method)
561 def RemoveMethod(self, function):
563 Removes the specified function's MethodWrapper from the
564 added_methods list, so we don't re-bind it when making a clone.
566 is_not_func = lambda dm, f=function: not dm.method is f
567 self.added_methods = filter(is_not_func, self.added_methods)
569 def Override(self, overrides):
571 Produce a modified environment whose variables are overriden by
572 the overrides dictionaries. "overrides" is a dictionary that
573 will override the variables of this environment.
575 This function is much more efficient than Clone() or creating
576 a new Environment because it doesn't copy the construction
577 environment dictionary, it just wraps the underlying construction
578 environment, and doesn't even create a wrapper object if there
581 if not overrides: return self
582 o = copy_non_reserved_keywords(overrides)
583 if not o: return self
586 for key, value in o.items():
587 if key == 'parse_flags':
590 overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
591 env = OverrideEnvironment(self, overrides)
592 if merges: env.MergeFlags(merges)
595 def ParseFlags(self, *flags):
597 Parse the set of flags and return a dict with the flags placed
598 in the appropriate entry. The flags are treated as a typical
599 set of command-line flags for a GNU-like toolchain and used to
600 populate the entries in the dict immediately below. If one of
601 the flag strings begins with a bang (exclamation mark), it is
602 assumed to be a command and the rest of the string is executed;
603 the result of that evaluation is then added to the dict.
606 'ASFLAGS' : SCons.Util.CLVar(''),
607 'CFLAGS' : SCons.Util.CLVar(''),
608 'CCFLAGS' : SCons.Util.CLVar(''),
610 'CPPFLAGS' : SCons.Util.CLVar(''),
612 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
613 'FRAMEWORKS' : SCons.Util.CLVar(''),
616 'LINKFLAGS' : SCons.Util.CLVar(''),
620 # The use of the "me" parameter to provide our own name for
621 # recursion is an egregious hack to support Python 2.1 and before.
622 def do_parse(arg, me, self = self, dict = dict):
623 # if arg is a sequence, recurse with each element
627 if not SCons.Util.is_String(arg):
628 for t in arg: me(t, me)
631 # if arg is a command, execute it
633 arg = self.backtick(arg[1:])
635 # utility function to deal with -D option
636 def append_define(name, dict = dict):
637 t = string.split(name, '=')
639 dict['CPPDEFINES'].append(name)
641 dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
643 # Loop through the flags and add them to the appropriate option.
644 # This tries to strike a balance between checking for all possible
645 # flags and keeping the logic to a finite size, so it doesn't
646 # check for some that don't occur often. It particular, if the
647 # flag is not known to occur in a config script and there's a way
648 # of passing the flag to the right place (by wrapping it in a -W
649 # flag, for example) we don't check for it. Note that most
650 # preprocessor options are not handled, since unhandled options
651 # are placed in CCFLAGS, so unless the preprocessor is invoked
652 # separately, these flags will still get to the preprocessor.
653 # Other options not currently handled:
654 # -iqoutedir (preprocessor search path)
655 # -u symbol (linker undefined symbol)
656 # -s (linker strip files)
657 # -static* (linker static binding)
658 # -shared* (linker dynamic binding)
659 # -symbolic (linker global binding)
660 # -R dir (deprecated linker rpath)
661 # IBM compilers may also accept -qframeworkdir=foo
663 params = shlex.split(arg)
664 append_next_arg_to = None # for multi-word args
666 if append_next_arg_to:
667 if append_next_arg_to == 'CPPDEFINES':
669 elif append_next_arg_to == '-include':
670 t = ('-include', self.fs.File(arg))
671 dict['CCFLAGS'].append(t)
672 elif append_next_arg_to == '-isysroot':
673 t = ('-isysroot', arg)
674 dict['CCFLAGS'].append(t)
675 dict['LINKFLAGS'].append(t)
676 elif append_next_arg_to == '-arch':
678 dict['CCFLAGS'].append(t)
679 dict['LINKFLAGS'].append(t)
681 dict[append_next_arg_to].append(arg)
682 append_next_arg_to = None
683 elif not arg[0] in ['-', '+']:
684 dict['LIBS'].append(self.fs.File(arg))
685 elif arg[:2] == '-L':
687 dict['LIBPATH'].append(arg[2:])
689 append_next_arg_to = 'LIBPATH'
690 elif arg[:2] == '-l':
692 dict['LIBS'].append(arg[2:])
694 append_next_arg_to = 'LIBS'
695 elif arg[:2] == '-I':
697 dict['CPPPATH'].append(arg[2:])
699 append_next_arg_to = 'CPPPATH'
700 elif arg[:4] == '-Wa,':
701 dict['ASFLAGS'].append(arg[4:])
702 dict['CCFLAGS'].append(arg)
703 elif arg[:4] == '-Wl,':
704 if arg[:11] == '-Wl,-rpath=':
705 dict['RPATH'].append(arg[11:])
706 elif arg[:7] == '-Wl,-R,':
707 dict['RPATH'].append(arg[7:])
708 elif arg[:6] == '-Wl,-R':
709 dict['RPATH'].append(arg[6:])
711 dict['LINKFLAGS'].append(arg)
712 elif arg[:4] == '-Wp,':
713 dict['CPPFLAGS'].append(arg)
714 elif arg[:2] == '-D':
716 append_define(arg[2:])
718 append_next_arg_to = 'CPPDEFINES'
719 elif arg == '-framework':
720 append_next_arg_to = 'FRAMEWORKS'
721 elif arg[:14] == '-frameworkdir=':
722 dict['FRAMEWORKPATH'].append(arg[14:])
723 elif arg[:2] == '-F':
725 dict['FRAMEWORKPATH'].append(arg[2:])
727 append_next_arg_to = 'FRAMEWORKPATH'
728 elif arg == '-mno-cygwin':
729 dict['CCFLAGS'].append(arg)
730 dict['LINKFLAGS'].append(arg)
731 elif arg == '-mwindows':
732 dict['LINKFLAGS'].append(arg)
733 elif arg == '-pthread':
734 dict['CCFLAGS'].append(arg)
735 dict['LINKFLAGS'].append(arg)
736 elif arg[:5] == '-std=':
737 dict['CFLAGS'].append(arg) # C only
739 dict['CCFLAGS'].append(arg)
740 dict['LINKFLAGS'].append(arg)
741 elif arg in ['-include', '-isysroot', '-arch']:
742 append_next_arg_to = arg
744 dict['CCFLAGS'].append(arg)
747 do_parse(arg, do_parse)
750 def MergeFlags(self, args, unique=1):
752 Merge the dict in args into the construction variables. If args
753 is not a dict, it is converted into a dict using ParseFlags.
754 If unique is not set, the flags are appended rather than merged.
757 if not SCons.Util.is_Dict(args):
758 args = self.ParseFlags(args)
760 apply(self.Append, (), args)
762 for key, value in args.items():
773 # Add orig and value. The logic here was lifted from
774 # part of env.Append() (see there for a lot of comments
775 # about the order in which things are tried) and is
776 # used mainly to handle coercion of strings to CLVar to
777 # "do the right thing" given (e.g.) an original CCFLAGS
778 # string variable like '-pipe -Wall'.
781 except (KeyError, TypeError):
783 add_to_orig = orig.append
784 except AttributeError:
785 value.insert(0, orig)
790 if key[-4:] == 'PATH':
791 ### keep left-most occurence
796 ### keep right-most occurence
804 # Used by the FindSourceFiles() method, below.
805 # Stuck here for support of pre-2.2 Python versions.
806 def build_source(ss, result):
808 if isinstance(s, SCons.Node.FS.Dir):
809 build_source(s.all_children(), result)
810 elif s.has_builder():
811 build_source(s.sources, result)
812 elif isinstance(s.disambiguate(), SCons.Node.FS.File):
815 def default_decide_source(dependency, target, prev_ni):
816 f = SCons.Defaults.DefaultEnvironment().decide_source
817 return f(dependency, target, prev_ni)
819 def default_decide_target(dependency, target, prev_ni):
820 f = SCons.Defaults.DefaultEnvironment().decide_target
821 return f(dependency, target, prev_ni)
823 def default_copy_from_cache(src, dst):
824 f = SCons.Defaults.DefaultEnvironment().copy_from_cache
827 class Base(SubstitutionEnvironment):
828 """Base class for "real" construction Environments. These are the
829 primary objects used to communicate dependency and construction
830 information to the build engine.
832 Keyword arguments supplied when the construction Environment
833 is created are construction variables used to initialize the
837 if SCons.Memoize.use_memoizer:
838 __metaclass__ = SCons.Memoize.Memoized_Metaclass
840 memoizer_counters = []
842 #######################################################################
843 # This is THE class for interacting with the SCons build engine,
844 # and it contains a lot of stuff, so we're going to try to keep this
845 # a little organized by grouping the methods.
846 #######################################################################
848 #######################################################################
849 # Methods that make an Environment act like a dictionary. These have
850 # the expected standard names for Python mapping objects. Note that
851 # we don't actually make an Environment a subclass of UserDict for
852 # performance reasons. Note also that we only supply methods for
853 # dictionary functionality that we actually need and use.
854 #######################################################################
864 Initialization of a basic SCons construction environment,
865 including setting up special construction variables like BUILDER,
866 PLATFORM, etc., and searching for and applying available Tools.
868 Note that we do *not* call the underlying base class
869 (SubsitutionEnvironment) initialization, because we need to
870 initialize things in a very specific order that doesn't work
871 with the much simpler base class initialization.
873 if __debug__: logInstanceCreation(self, 'Environment.Base')
875 self.fs = SCons.Node.FS.get_default_fs()
876 self.ans = SCons.Node.Alias.default_ans
877 self.lookup_list = SCons.Node.arg2nodes_lookups
878 self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment)
880 self.added_methods = []
882 # We don't use AddMethod, or define these as methods in this
883 # class, because we *don't* want these functions to be bound
884 # methods. They need to operate independently so that the
885 # settings will work properly regardless of whether a given
886 # target ends up being built with a Base environment or an
887 # OverrideEnvironment or what have you.
888 self.decide_target = default_decide_target
889 self.decide_source = default_decide_source
891 self.copy_from_cache = default_copy_from_cache
893 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
896 platform = self._dict.get('PLATFORM', None)
898 platform = SCons.Platform.Platform()
899 if SCons.Util.is_String(platform):
900 platform = SCons.Platform.Platform(platform)
901 self._dict['PLATFORM'] = str(platform)
904 # Apply the passed-in variables and customizable options to the
905 # environment before calling the tools, because they may use
906 # some of them during initialization.
907 apply(self.Replace, (), kw)
910 keys = keys + options.keys()
916 save[k] = self._dict[k]
918 # No value may have been set if they tried to pass in a
919 # reserved variable name like TARGETS.
922 SCons.Tool.Initializers(self)
925 tools = self._dict.get('TOOLS', None)
928 apply_tools(self, tools, toolpath)
930 # Now restore the passed-in variables and customized options
931 # to the environment, since the values the user set explicitly
932 # should override any values set by the tools.
933 for key, val in save.items():
934 self._dict[key] = val
936 # Finally, apply any flags to be merged in
937 if parse_flags: self.MergeFlags(parse_flags)
939 #######################################################################
940 # Utility methods that are primarily for internal use by SCons.
941 # These begin with lower-case letters.
942 #######################################################################
944 def get_builder(self, name):
945 """Fetch the builder with the specified name from the environment.
948 return self._dict['BUILDERS'][name]
952 def get_CacheDir(self):
954 path = self._CacheDir_path
955 except AttributeError:
956 path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
958 if path == self._last_CacheDir_path:
959 return self._last_CacheDir
960 except AttributeError:
962 cd = SCons.CacheDir.CacheDir(path)
963 self._last_CacheDir_path = path
964 self._last_CacheDir = cd
967 def get_factory(self, factory, default='File'):
968 """Return a factory function for creating Nodes for this
969 construction environment.
973 is_node = issubclass(factory, SCons.Node.Node)
975 # The specified factory isn't a Node itself--it's
976 # most likely None, or possibly a callable.
980 # The specified factory is a Node (sub)class. Try to
981 # return the FS method that corresponds to the Node's
982 # name--that is, we return self.fs.Dir if they want a Dir,
983 # self.fs.File for a File, etc.
984 try: name = factory.__name__
985 except AttributeError: pass
988 # They passed us None, or we picked up a name from a specified
989 # class, so return the FS method. (Note that we *don't*
990 # use our own self.{Dir,File} methods because that would
991 # cause env.subst() to be called twice on the file name,
992 # interfering with files that have $$ in them.)
993 factory = getattr(self.fs, name)
996 memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
1000 return self._memo['_gsm']
1007 scanners = self._dict['SCANNERS']
1011 # Reverse the scanner list so that, if multiple scanners
1012 # claim they can scan the same suffix, earlier scanners
1013 # in the list will overwrite later scanners, so that
1014 # the result looks like a "first match" to the user.
1015 if not SCons.Util.is_List(scanners):
1016 scanners = [scanners]
1018 scanners = scanners[:] # copy so reverse() doesn't mod original
1020 for scanner in scanners:
1021 for k in scanner.get_skeys(self):
1024 self._memo['_gsm'] = result
1028 def get_scanner(self, skey):
1029 """Find the appropriate scanner given a key (usually a file suffix).
1031 return self._gsm().get(skey)
1033 def scanner_map_delete(self, kw=None):
1034 """Delete the cached scanner map (if we need to).
1037 del self._memo['_gsm']
1041 def _update(self, dict):
1042 """Update an environment's values directly, bypassing the normal
1043 checks that occur when users try to set items.
1045 self._dict.update(dict)
1047 def get_src_sig_type(self):
1049 return self.src_sig_type
1050 except AttributeError:
1051 t = SCons.Defaults.DefaultEnvironment().src_sig_type
1052 self.src_sig_type = t
1055 def get_tgt_sig_type(self):
1057 return self.tgt_sig_type
1058 except AttributeError:
1059 t = SCons.Defaults.DefaultEnvironment().tgt_sig_type
1060 self.tgt_sig_type = t
1063 #######################################################################
1064 # Public methods for manipulating an Environment. These begin with
1065 # upper-case letters. The essential characteristic of methods in
1066 # this section is that they do *not* have corresponding same-named
1067 # global functions. For example, a stand-alone Append() function
1068 # makes no sense, because Append() is all about appending values to
1069 # an Environment's construction variables.
1070 #######################################################################
1072 def Append(self, **kw):
1073 """Append values to existing construction variables
1076 kw = copy_non_reserved_keywords(kw)
1077 for key, val in kw.items():
1078 # It would be easier on the eyes to write this using
1079 # "continue" statements whenever we finish processing an item,
1080 # but Python 1.5.2 apparently doesn't let you use "continue"
1081 # within try:-except: blocks, so we have to nest our code.
1083 orig = self._dict[key]
1085 # No existing variable in the environment, so just set
1086 # it to the new value.
1087 self._dict[key] = val
1090 # Check if the original looks like a dictionary.
1091 # If it is, we can't just try adding the value because
1092 # dictionaries don't have __add__() methods, and
1093 # things like UserList will incorrectly coerce the
1094 # original dict to a list (which we don't want).
1095 update_dict = orig.update
1096 except AttributeError:
1098 # Most straightforward: just try to add them
1099 # together. This will work in most cases, when the
1100 # original and new values are of compatible types.
1101 self._dict[key] = orig + val
1102 except (KeyError, TypeError):
1104 # Check if the original is a list.
1105 add_to_orig = orig.append
1106 except AttributeError:
1107 # The original isn't a list, but the new
1108 # value is (by process of elimination),
1109 # so insert the original in the new value
1110 # (if there's one to insert) and replace
1111 # the variable with it.
1114 self._dict[key] = val
1116 # The original is a list, so append the new
1117 # value to it (if there's a value to append).
1121 # The original looks like a dictionary, so update it
1122 # based on what we think the value looks like.
1123 if SCons.Util.is_List(val):
1129 except (AttributeError, TypeError, ValueError):
1130 if SCons.Util.is_Dict(val):
1131 for k, v in val.items():
1135 self.scanner_map_delete(kw)
1137 def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
1138 """Append path elements to the path 'name' in the 'ENV'
1139 dictionary for this environment. Will only add any particular
1140 path once, and will normpath and normcase all paths to help
1141 assure this. This can also handle the case where the env
1142 variable is a list instead of a string.
1146 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1147 orig = self._dict[envname][name]
1149 nv = SCons.Util.AppendPath(orig, newpath, sep)
1151 if not self._dict.has_key(envname):
1152 self._dict[envname] = {}
1154 self._dict[envname][name] = nv
1156 def AppendUnique(self, **kw):
1157 """Append values to existing construction variables
1158 in an Environment, if they're not already there.
1160 kw = copy_non_reserved_keywords(kw)
1161 for key, val in kw.items():
1162 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1163 self._dict[key] = val
1164 elif SCons.Util.is_Dict(self._dict[key]) and \
1165 SCons.Util.is_Dict(val):
1166 self._dict[key].update(val)
1167 elif SCons.Util.is_List(val):
1168 dk = self._dict[key]
1169 if not SCons.Util.is_List(dk):
1171 val = filter(lambda x, dk=dk: x not in dk, val)
1172 self._dict[key] = dk + val
1174 dk = self._dict[key]
1175 if SCons.Util.is_List(dk):
1176 # By elimination, val is not a list. Since dk is a
1177 # list, wrap val in a list first.
1179 self._dict[key] = dk + [val]
1181 self._dict[key] = self._dict[key] + val
1182 self.scanner_map_delete(kw)
1184 def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
1185 """Return a copy of a construction Environment. The
1186 copy is like a Python "deep copy"--that is, independent
1187 copies are made recursively of each objects--except that
1188 a reference is copied when an object is not deep-copyable
1189 (like a function). There are no references to any mutable
1190 objects in the original Environment.
1192 clone = copy.copy(self)
1193 clone._dict = semi_deepcopy(self._dict)
1196 cbd = clone._dict['BUILDERS']
1200 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
1202 # Check the methods added via AddMethod() and re-bind them to
1203 # the cloned environment. Only do this if the attribute hasn't
1204 # been overwritten by the user explicitly and still points to
1206 clone.added_methods = []
1207 for mw in self.added_methods:
1208 if mw == getattr(self, mw.name):
1209 clone.added_methods.append(mw.clone(clone))
1213 # Apply passed-in variables before the tools
1214 # so the tools can use the new variables
1215 kw = copy_non_reserved_keywords(kw)
1217 for key, value in kw.items():
1218 new[key] = SCons.Subst.scons_subst_once(value, self, key)
1219 apply(clone.Replace, (), new)
1221 apply_tools(clone, tools, toolpath)
1223 # apply them again in case the tools overwrote them
1224 apply(clone.Replace, (), new)
1226 # Finally, apply any flags to be merged in
1227 if parse_flags: clone.MergeFlags(parse_flags)
1229 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
1232 def Copy(self, *args, **kw):
1233 global _warn_copy_deprecated
1234 if _warn_copy_deprecated:
1235 msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
1236 SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
1237 _warn_copy_deprecated = False
1238 return apply(self.Clone, args, kw)
1240 def _changed_build(self, dependency, target, prev_ni):
1241 if dependency.changed_state(target, prev_ni):
1243 return self.decide_source(dependency, target, prev_ni)
1245 def _changed_content(self, dependency, target, prev_ni):
1246 return dependency.changed_content(target, prev_ni)
1248 def _changed_source(self, dependency, target, prev_ni):
1249 target_env = dependency.get_build_env()
1250 type = target_env.get_tgt_sig_type()
1251 if type == 'source':
1252 return target_env.decide_source(dependency, target, prev_ni)
1254 return target_env.decide_target(dependency, target, prev_ni)
1256 def _changed_timestamp_then_content(self, dependency, target, prev_ni):
1257 return dependency.changed_timestamp_then_content(target, prev_ni)
1259 def _changed_timestamp_newer(self, dependency, target, prev_ni):
1260 return dependency.changed_timestamp_newer(target, prev_ni)
1262 def _changed_timestamp_match(self, dependency, target, prev_ni):
1263 return dependency.changed_timestamp_match(target, prev_ni)
1265 def _copy_from_cache(self, src, dst):
1266 return self.fs.copy(src, dst)
1268 def _copy2_from_cache(self, src, dst):
1269 return self.fs.copy2(src, dst)
1271 def Decider(self, function):
1272 copy_function = self._copy2_from_cache
1273 if function in ('MD5', 'content'):
1274 if not SCons.Util.md5:
1275 raise UserError, "MD5 signatures are not available in this version of Python."
1276 function = self._changed_content
1277 elif function == 'MD5-timestamp':
1278 function = self._changed_timestamp_then_content
1279 elif function in ('timestamp-newer', 'make'):
1280 function = self._changed_timestamp_newer
1281 copy_function = self._copy_from_cache
1282 elif function == 'timestamp-match':
1283 function = self._changed_timestamp_match
1284 elif not callable(function):
1285 raise UserError, "Unknown Decider value %s" % repr(function)
1287 # We don't use AddMethod because we don't want to turn the
1288 # function, which only expects three arguments, into a bound
1289 # method, which would add self as an initial, fourth argument.
1290 self.decide_target = function
1291 self.decide_source = function
1293 self.copy_from_cache = copy_function
1295 def Detect(self, progs):
1296 """Return the first available program in progs.
1298 if not SCons.Util.is_List(progs):
1301 path = self.WhereIs(prog)
1302 if path: return prog
1305 def Dictionary(self, *args):
1308 dlist = map(lambda x, s=self: s._dict[x], args)
1313 def Dump(self, key = None):
1315 Using the standard Python pretty printer, dump the contents of the
1316 scons build environment to stdout.
1318 If the key passed in is anything other than None, then that will
1319 be used as an index into the build environment dictionary and
1320 whatever is found there will be fed into the pretty printer. Note
1321 that this key is case sensitive.
1324 pp = pprint.PrettyPrinter(indent=2)
1326 dict = self.Dictionary(key)
1328 dict = self.Dictionary()
1329 return pp.pformat(dict)
1331 def FindIxes(self, paths, prefix, suffix):
1333 Search a list of paths for something that matches the prefix and suffix.
1335 paths - the list of paths or nodes.
1336 prefix - construction variable for the prefix.
1337 suffix - construction variable for the suffix.
1340 suffix = self.subst('$'+suffix)
1341 prefix = self.subst('$'+prefix)
1344 dir,name = os.path.split(str(path))
1345 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
1348 def ParseConfig(self, command, function=None, unique=1):
1350 Use the specified function to parse the output of the command
1351 in order to modify the current environment. The 'command' can
1352 be a string or a list of strings representing a command and
1353 its arguments. 'Function' is an optional argument that takes
1354 the environment, the output of the command, and the unique flag.
1355 If no function is specified, MergeFlags, which treats the output
1356 as the result of a typical 'X-config' command (i.e. gtk-config),
1357 will merge the output into the appropriate variables.
1359 if function is None:
1360 def parse_conf(env, cmd, unique=unique):
1361 return env.MergeFlags(cmd, unique)
1362 function = parse_conf
1363 if SCons.Util.is_List(command):
1364 command = string.join(command)
1365 command = self.subst(command)
1366 return function(self, self.backtick(command))
1368 def ParseDepends(self, filename, must_exist=None, only_one=0):
1370 Parse a mkdep-style file for explicit dependencies. This is
1371 completely abusable, and should be unnecessary in the "normal"
1372 case of proper SCons configuration, but it may help make
1373 the transition from a Make hierarchy easier for some people
1374 to swallow. It can also be genuinely useful when using a tool
1375 that can write a .d file, but for which writing a scanner would
1378 filename = self.subst(filename)
1380 fp = open(filename, 'r')
1385 lines = SCons.Util.LogicalLines(fp).readlines()
1386 lines = filter(lambda l: l[0] != '#', lines)
1390 target, depends = string.split(line, ':', 1)
1391 except (AttributeError, TypeError, ValueError):
1392 # Python 1.5.2 throws TypeError if line isn't a string,
1393 # Python 2.x throws AttributeError because it tries
1394 # to call line.split(). Either can throw ValueError
1395 # if the line doesn't split into two or more elements.
1398 tdlist.append((string.split(target), string.split(depends)))
1400 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
1401 if len(targets) > 1:
1402 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
1403 for target, depends in tdlist:
1404 self.Depends(target, depends)
1406 def Platform(self, platform):
1407 platform = self.subst(platform)
1408 return SCons.Platform.Platform(platform)(self)
1410 def Prepend(self, **kw):
1411 """Prepend values to existing construction variables
1414 kw = copy_non_reserved_keywords(kw)
1415 for key, val in kw.items():
1416 # It would be easier on the eyes to write this using
1417 # "continue" statements whenever we finish processing an item,
1418 # but Python 1.5.2 apparently doesn't let you use "continue"
1419 # within try:-except: blocks, so we have to nest our code.
1421 orig = self._dict[key]
1423 # No existing variable in the environment, so just set
1424 # it to the new value.
1425 self._dict[key] = val
1428 # Check if the original looks like a dictionary.
1429 # If it is, we can't just try adding the value because
1430 # dictionaries don't have __add__() methods, and
1431 # things like UserList will incorrectly coerce the
1432 # original dict to a list (which we don't want).
1433 update_dict = orig.update
1434 except AttributeError:
1436 # Most straightforward: just try to add them
1437 # together. This will work in most cases, when the
1438 # original and new values are of compatible types.
1439 self._dict[key] = val + orig
1440 except (KeyError, TypeError):
1442 # Check if the added value is a list.
1443 add_to_val = val.append
1444 except AttributeError:
1445 # The added value isn't a list, but the
1446 # original is (by process of elimination),
1447 # so insert the the new value in the original
1448 # (if there's one to insert).
1452 # The added value is a list, so append
1453 # the original to it (if there's a value
1457 self._dict[key] = val
1459 # The original looks like a dictionary, so update it
1460 # based on what we think the value looks like.
1461 if SCons.Util.is_List(val):
1467 except (AttributeError, TypeError, ValueError):
1468 if SCons.Util.is_Dict(val):
1469 for k, v in val.items():
1473 self.scanner_map_delete(kw)
1475 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
1476 """Prepend path elements to the path 'name' in the 'ENV'
1477 dictionary for this environment. Will only add any particular
1478 path once, and will normpath and normcase all paths to help
1479 assure this. This can also handle the case where the env
1480 variable is a list instead of a string.
1484 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1485 orig = self._dict[envname][name]
1487 nv = SCons.Util.PrependPath(orig, newpath, sep)
1489 if not self._dict.has_key(envname):
1490 self._dict[envname] = {}
1492 self._dict[envname][name] = nv
1494 def PrependUnique(self, **kw):
1495 """Append values to existing construction variables
1496 in an Environment, if they're not already there.
1498 kw = copy_non_reserved_keywords(kw)
1499 for key, val in kw.items():
1500 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1501 self._dict[key] = val
1502 elif SCons.Util.is_Dict(self._dict[key]) and \
1503 SCons.Util.is_Dict(val):
1504 self._dict[key].update(val)
1505 elif SCons.Util.is_List(val):
1506 dk = self._dict[key]
1507 if not SCons.Util.is_List(dk):
1509 val = filter(lambda x, dk=dk: x not in dk, val)
1510 self._dict[key] = val + dk
1512 dk = self._dict[key]
1513 if SCons.Util.is_List(dk):
1514 # By elimination, val is not a list. Since dk is a
1515 # list, wrap val in a list first.
1517 self._dict[key] = [val] + dk
1519 self._dict[key] = val + dk
1520 self.scanner_map_delete(kw)
1522 def Replace(self, **kw):
1523 """Replace existing construction variables in an Environment
1524 with new construction variables and/or values.
1527 kwbd = kw['BUILDERS']
1531 kwbd = semi_deepcopy(kwbd)
1533 self.__setitem__('BUILDERS', kwbd)
1534 kw = copy_non_reserved_keywords(kw)
1535 self._update(semi_deepcopy(kw))
1536 self.scanner_map_delete(kw)
1538 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1540 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1542 env - Environment used to interpolate variables.
1543 path - the path that will be modified.
1544 old_prefix - construction variable for the old prefix.
1545 old_suffix - construction variable for the old suffix.
1546 new_prefix - construction variable for the new prefix.
1547 new_suffix - construction variable for the new suffix.
1549 old_prefix = self.subst('$'+old_prefix)
1550 old_suffix = self.subst('$'+old_suffix)
1552 new_prefix = self.subst('$'+new_prefix)
1553 new_suffix = self.subst('$'+new_suffix)
1555 dir,name = os.path.split(str(path))
1556 if name[:len(old_prefix)] == old_prefix:
1557 name = name[len(old_prefix):]
1558 if name[-len(old_suffix):] == old_suffix:
1559 name = name[:-len(old_suffix)]
1560 return os.path.join(dir, new_prefix+name+new_suffix)
1562 def SetDefault(self, **kw):
1564 if self._dict.has_key(k):
1566 apply(self.Replace, (), kw)
1568 def _find_toolpath_dir(self, tp):
1569 return self.fs.Dir(self.subst(tp)).srcnode().abspath
1571 def Tool(self, tool, toolpath=None, **kw):
1572 if SCons.Util.is_String(tool):
1573 tool = self.subst(tool)
1574 if toolpath is None:
1575 toolpath = self.get('toolpath', [])
1576 toolpath = map(self._find_toolpath_dir, toolpath)
1577 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
1580 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1581 """Find prog in the path.
1585 path = self['ENV']['PATH']
1588 elif SCons.Util.is_String(path):
1589 path = self.subst(path)
1592 pathext = self['ENV']['PATHEXT']
1595 elif SCons.Util.is_String(pathext):
1596 pathext = self.subst(pathext)
1597 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1598 if path: return path
1601 #######################################################################
1602 # Public methods for doing real "SCons stuff" (manipulating
1603 # dependencies, setting attributes on targets, etc.). These begin
1604 # with upper-case letters. The essential characteristic of methods
1605 # in this section is that they all *should* have corresponding
1606 # same-named global functions.
1607 #######################################################################
1609 def Action(self, *args, **kw):
1610 def subst_string(a, self=self):
1611 if SCons.Util.is_String(a):
1614 nargs = map(subst_string, args)
1615 nkw = self.subst_kw(kw)
1616 return apply(SCons.Action.Action, nargs, nkw)
1618 def AddPreAction(self, files, action):
1619 nodes = self.arg2nodes(files, self.fs.Entry)
1620 action = SCons.Action.Action(action)
1622 for executor in map(lambda n: n.get_executor(), nodes):
1624 for executor in uniq.keys():
1625 executor.add_pre_action(action)
1628 def AddPostAction(self, files, action):
1629 nodes = self.arg2nodes(files, self.fs.Entry)
1630 action = SCons.Action.Action(action)
1632 for executor in map(lambda n: n.get_executor(), nodes):
1634 for executor in uniq.keys():
1635 executor.add_post_action(action)
1638 def Alias(self, target, source=[], action=None, **kw):
1639 tlist = self.arg2nodes(target, self.ans.Alias)
1640 if not SCons.Util.is_List(source):
1642 source = filter(None, source)
1646 # There are no source files and no action, so just
1647 # return a target list of classic Alias Nodes, without
1648 # any builder. The externally visible effect is that
1649 # this will make the wrapping Script.BuildTask class
1650 # say that there's "Nothing to be done" for this Alias,
1651 # instead of that it's "up to date."
1654 # No action, but there are sources. Re-call all the target
1655 # builders to add the sources to each target.
1658 bld = t.get_builder(AliasBuilder)
1659 result.extend(bld(self, t, source))
1662 nkw = self.subst_kw(kw)
1664 'action' : SCons.Action.Action(action),
1665 'source_factory' : self.fs.Entry,
1667 'is_explicit' : None,
1669 bld = apply(SCons.Builder.Builder, (), nkw)
1671 # Apply the Builder separately to each target so that the Aliases
1672 # stay separate. If we did one "normal" Builder call with the
1673 # whole target list, then all of the target Aliases would be
1674 # associated under a single Executor.
1677 # Calling the convert() method will cause a new Executor to be
1678 # created from scratch, so we have to explicitly initialize
1679 # it with the target's existing sources, plus our new ones,
1680 # so nothing gets lost.
1682 if b is None or b is AliasBuilder:
1685 nkw['action'] = b.action + action
1686 b = apply(SCons.Builder.Builder, (), nkw)
1688 result.extend(b(self, t, t.sources + source))
1691 def AlwaysBuild(self, *targets):
1694 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1696 t.set_always_build()
1699 def BuildDir(self, *args, **kw):
1700 if kw.has_key('build_dir'):
1701 kw['variant_dir'] = kw['build_dir']
1703 return apply(self.VariantDir, args, kw)
1705 def Builder(self, **kw):
1706 nkw = self.subst_kw(kw)
1707 return apply(SCons.Builder.Builder, [], nkw)
1709 def CacheDir(self, path):
1710 import SCons.CacheDir
1711 if not path is None:
1712 path = self.subst(path)
1713 self._CacheDir_path = path
1715 def Clean(self, targets, files):
1717 tlist = self.arg2nodes(targets, self.fs.Entry)
1718 flist = self.arg2nodes(files, self.fs.Entry)
1721 CleanTargets[t].extend(flist)
1723 CleanTargets[t] = flist
1725 def Configure(self, *args, **kw):
1728 nargs = nargs + self.subst_list(args)[0]
1729 nkw = self.subst_kw(kw)
1730 nkw['_depth'] = kw.get('_depth', 0) + 1
1732 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1735 return apply(SCons.SConf.SConf, nargs, nkw)
1737 def Command(self, target, source, action, **kw):
1738 """Builds the supplied target files from the supplied
1739 source files using the supplied action. Action may
1740 be any type that the Builder constructor will accept
1744 'target_factory' : self.fs.Entry,
1745 'source_factory' : self.fs.Entry,
1747 try: bkw['source_scanner'] = kw['source_scanner']
1748 except KeyError: pass
1749 else: del kw['source_scanner']
1750 bld = apply(SCons.Builder.Builder, (), bkw)
1751 return apply(bld, (self, target, source), kw)
1753 def Depends(self, target, dependency):
1754 """Explicity specify that 'target's depend on 'dependency'."""
1755 tlist = self.arg2nodes(target, self.fs.Entry)
1756 dlist = self.arg2nodes(dependency, self.fs.Entry)
1758 t.add_dependency(dlist)
1761 def Dir(self, name, *args, **kw):
1764 s = self.subst(name)
1765 if SCons.Util.is_Sequence(s):
1768 result.append(apply(self.fs.Dir, (e,) + args, kw))
1770 return apply(self.fs.Dir, (s,) + args, kw)
1772 def NoClean(self, *targets):
1773 """Tags a target so that it will not be cleaned by -c"""
1776 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1781 def NoCache(self, *targets):
1782 """Tags a target so that it will not be cached"""
1785 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1790 def Entry(self, name, *args, **kw):
1793 s = self.subst(name)
1794 if SCons.Util.is_Sequence(s):
1797 result.append(apply(self.fs.Entry, (e,) + args, kw))
1799 return apply(self.fs.Entry, (s,) + args, kw)
1801 def Environment(self, **kw):
1802 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1804 def Execute(self, action, *args, **kw):
1805 """Directly execute an action through an Environment
1807 action = apply(self.Action, (action,) + args, kw)
1808 result = action([], [], self)
1809 if isinstance(result, SCons.Errors.BuildError):
1810 return result.status
1814 def File(self, name, *args, **kw):
1817 s = self.subst(name)
1818 if SCons.Util.is_Sequence(s):
1821 result.append(apply(self.fs.File, (e,) + args, kw))
1823 return apply(self.fs.File, (s,) + args, kw)
1825 def FindFile(self, file, dirs):
1826 file = self.subst(file)
1827 nodes = self.arg2nodes(dirs, self.fs.Dir)
1828 return SCons.Node.FS.find_file(file, tuple(nodes))
1830 def Flatten(self, sequence):
1831 return SCons.Util.flatten(sequence)
1833 def GetBuildPath(self, files):
1834 result = map(str, self.arg2nodes(files, self.fs.Entry))
1835 if SCons.Util.is_List(files):
1840 def Glob(self, pattern, ondisk=True, source=False, strings=False):
1841 return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
1843 def Ignore(self, target, dependency):
1844 """Ignore a dependency."""
1845 tlist = self.arg2nodes(target, self.fs.Entry)
1846 dlist = self.arg2nodes(dependency, self.fs.Entry)
1851 def Literal(self, string):
1852 return SCons.Subst.Literal(string)
1854 def Local(self, *targets):
1856 for targ in targets:
1857 if isinstance(targ, SCons.Node.Node):
1861 for t in self.arg2nodes(targ, self.fs.Entry):
1866 def Precious(self, *targets):
1869 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1874 def Repository(self, *dirs, **kw):
1875 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1876 apply(self.fs.Repository, dirs, kw)
1878 def Requires(self, target, prerequisite):
1879 """Specify that 'prerequisite' must be built before 'target',
1880 (but 'target' does not actually depend on 'prerequisite'
1881 and need not be rebuilt if it changes)."""
1882 tlist = self.arg2nodes(target, self.fs.Entry)
1883 plist = self.arg2nodes(prerequisite, self.fs.Entry)
1885 t.add_prerequisite(plist)
1888 def Scanner(self, *args, **kw):
1891 if SCons.Util.is_String(arg):
1892 arg = self.subst(arg)
1894 nkw = self.subst_kw(kw)
1895 return apply(SCons.Scanner.Base, nargs, nkw)
1897 def SConsignFile(self, name=".sconsign", dbm_module=None):
1898 if not name is None:
1899 name = self.subst(name)
1900 if not os.path.isabs(name):
1901 name = os.path.join(str(self.fs.SConstruct_dir), name)
1902 SCons.SConsign.File(name, dbm_module)
1904 def SideEffect(self, side_effect, target):
1905 """Tell scons that side_effects are built as side
1906 effects of building targets."""
1907 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1908 targets = self.arg2nodes(target, self.fs.Entry)
1910 for side_effect in side_effects:
1911 if side_effect.multiple_side_effect_has_builder():
1912 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1913 side_effect.add_source(targets)
1914 side_effect.side_effect = 1
1915 self.Precious(side_effect)
1916 for target in targets:
1917 target.side_effects.append(side_effect)
1920 def SourceCode(self, entry, builder):
1921 """Arrange for a source code builder for (part of) a tree."""
1922 entries = self.arg2nodes(entry, self.fs.Entry)
1923 for entry in entries:
1924 entry.set_src_builder(builder)
1927 def SourceSignatures(self, type):
1928 global _warn_source_signatures_deprecated
1929 if _warn_source_signatures_deprecated:
1930 msg = "The env.SourceSignatures() method is deprecated;\n" + \
1931 "\tconvert your build to use the env.Decider() method instead."
1932 SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
1933 _warn_source_signatures_deprecated = False
1934 type = self.subst(type)
1935 self.src_sig_type = type
1937 if not SCons.Util.md5:
1938 raise UserError, "MD5 signatures are not available in this version of Python."
1939 self.decide_source = self._changed_content
1940 elif type == 'timestamp':
1941 self.decide_source = self._changed_timestamp_match
1943 raise UserError, "Unknown source signature type '%s'" % type
1945 def Split(self, arg):
1946 """This function converts a string or list into a list of strings
1947 or Nodes. This makes things easier for users by allowing files to
1948 be specified as a white-space separated list to be split.
1949 The input rules are:
1950 - A single string containing names separated by spaces. These will be
1951 split apart at the spaces.
1952 - A single Node instance
1953 - A list containing either strings or Node instances. Any strings
1954 in the list are not split at spaces.
1955 In all cases, the function returns a list of Nodes and strings."""
1956 if SCons.Util.is_List(arg):
1957 return map(self.subst, arg)
1958 elif SCons.Util.is_String(arg):
1959 return string.split(self.subst(arg))
1961 return [self.subst(arg)]
1963 def TargetSignatures(self, type):
1964 global _warn_target_signatures_deprecated
1965 if _warn_target_signatures_deprecated:
1966 msg = "The env.TargetSignatures() method is deprecated;\n" + \
1967 "\tconvert your build to use the env.Decider() method instead."
1968 SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
1969 _warn_target_signatures_deprecated = False
1970 type = self.subst(type)
1971 self.tgt_sig_type = type
1972 if type in ('MD5', 'content'):
1973 if not SCons.Util.md5:
1974 raise UserError, "MD5 signatures are not available in this version of Python."
1975 self.decide_target = self._changed_content
1976 elif type == 'timestamp':
1977 self.decide_target = self._changed_timestamp_match
1978 elif type == 'build':
1979 self.decide_target = self._changed_build
1980 elif type == 'source':
1981 self.decide_target = self._changed_source
1983 raise UserError, "Unknown target signature type '%s'"%type
1985 def Value(self, value, built_value=None):
1988 return SCons.Node.Python.Value(value, built_value)
1990 def VariantDir(self, variant_dir, src_dir, duplicate=1):
1991 variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
1992 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1993 self.fs.VariantDir(variant_dir, src_dir, duplicate)
1995 def FindSourceFiles(self, node='.'):
1996 """ returns a list of all source files.
1998 node = self.arg2nodes(node, self.fs.Entry)[0]
2001 # Uncomment this and get rid of the global definition when we
2002 # drop support for pre-2.2 Python versions.
2003 #def build_source(ss, result):
2005 # if isinstance(s, SCons.Node.FS.Dir):
2006 # build_source(s.all_children(), result)
2007 # elif s.has_builder():
2008 # build_source(s.sources, result)
2009 # elif isinstance(s.disambiguate(), SCons.Node.FS.File):
2011 build_source(node.all_children(), sources)
2013 # now strip the build_node from the sources by calling the srcnode
2015 def get_final_srcnode(file):
2016 srcnode = file.srcnode()
2017 while srcnode != file.srcnode():
2018 srcnode = file.srcnode()
2021 # get the final srcnode for all nodes, this means stripping any
2022 # attached build node.
2023 map( get_final_srcnode, sources )
2026 return list(set(sources))
2028 def FindInstalledFiles(self):
2029 """ returns the list of all targets of the Install and InstallAs Builder.
2031 from SCons.Tool import install
2032 if install._UNIQUE_INSTALLED_FILES is None:
2033 install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
2034 return install._UNIQUE_INSTALLED_FILES
2036 class OverrideEnvironment(Base):
2037 """A proxy that overrides variables in a wrapped construction
2038 environment by returning values from an overrides dictionary in
2039 preference to values from the underlying subject environment.
2041 This is a lightweight (I hope) proxy that passes through most use of
2042 attributes to the underlying Environment.Base class, but has just
2043 enough additional methods defined to act like a real construction
2044 environment with overridden values. It can wrap either a Base
2045 construction environment, or another OverrideEnvironment, which
2046 can in turn nest arbitrary OverrideEnvironments...
2048 Note that we do *not* call the underlying base class
2049 (SubsitutionEnvironment) initialization, because we get most of those
2050 from proxying the attributes of the subject construction environment.
2051 But because we subclass SubstitutionEnvironment, this class also
2052 has inherited arg2nodes() and subst*() methods; those methods can't
2053 be proxied because they need *this* object's methods to fetch the
2054 values from the overrides dictionary.
2057 if SCons.Memoize.use_memoizer:
2058 __metaclass__ = SCons.Memoize.Memoized_Metaclass
2060 def __init__(self, subject, overrides={}):
2061 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
2062 self.__dict__['__subject'] = subject
2063 self.__dict__['overrides'] = overrides
2065 # Methods that make this class act like a proxy.
2066 def __getattr__(self, name):
2067 return getattr(self.__dict__['__subject'], name)
2068 def __setattr__(self, name, value):
2069 setattr(self.__dict__['__subject'], name, value)
2071 # Methods that make this class act like a dictionary.
2072 def __getitem__(self, key):
2074 return self.__dict__['overrides'][key]
2076 return self.__dict__['__subject'].__getitem__(key)
2077 def __setitem__(self, key, value):
2078 if not is_valid_construction_var(key):
2079 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
2080 self.__dict__['overrides'][key] = value
2081 def __delitem__(self, key):
2083 del self.__dict__['overrides'][key]
2089 result = self.__dict__['__subject'].__delitem__(key)
2095 def get(self, key, default=None):
2096 """Emulates the get() method of dictionaries."""
2098 return self.__dict__['overrides'][key]
2100 return self.__dict__['__subject'].get(key, default)
2101 def has_key(self, key):
2103 self.__dict__['overrides'][key]
2106 return self.__dict__['__subject'].has_key(key)
2107 def Dictionary(self):
2108 """Emulates the items() method of dictionaries."""
2109 d = self.__dict__['__subject'].Dictionary().copy()
2110 d.update(self.__dict__['overrides'])
2113 """Emulates the items() method of dictionaries."""
2114 return self.Dictionary().items()
2116 # Overridden private construction environment methods.
2117 def _update(self, dict):
2118 """Update an environment's values directly, bypassing the normal
2119 checks that occur when users try to set items.
2121 self.__dict__['overrides'].update(dict)
2124 return self.__dict__['__subject'].gvars()
2127 lvars = self.__dict__['__subject'].lvars()
2128 lvars.update(self.__dict__['overrides'])
2131 # Overridden public construction environment methods.
2132 def Replace(self, **kw):
2133 kw = copy_non_reserved_keywords(kw)
2134 self.__dict__['overrides'].update(semi_deepcopy(kw))
2136 # The entry point that will be used by the external world
2137 # to refer to a construction environment. This allows the wrapper
2138 # interface to extend a construction environment for its own purposes
2139 # by subclassing SCons.Environment.Base and then assigning the
2140 # class to SCons.Environment.Environment.
2144 # An entry point for returning a proxy subclass instance that overrides
2145 # the subst*() methods so they don't actually perform construction
2146 # variable substitution. This is specifically intended to be the shim
2147 # layer in between global function calls (which don't want construction
2148 # variable substitution) and the DefaultEnvironment() (which would
2149 # substitute variables if left to its own devices)."""
2151 # We have to wrap this in a function that allows us to delay definition of
2152 # the class until it's necessary, so that when it subclasses Environment
2153 # it will pick up whatever Environment subclass the wrapper interface
2154 # might have assigned to SCons.Environment.Environment.
2156 def NoSubstitutionProxy(subject):
2157 class _NoSubstitutionProxy(Environment):
2158 def __init__(self, subject):
2159 self.__dict__['__subject'] = subject
2160 def __getattr__(self, name):
2161 return getattr(self.__dict__['__subject'], name)
2162 def __setattr__(self, name, value):
2163 return setattr(self.__dict__['__subject'], name, value)
2164 def raw_to_mode(self, dict):
2172 def subst(self, string, *args, **kwargs):
2174 def subst_kw(self, kw, *args, **kwargs):
2176 def subst_list(self, string, *args, **kwargs):
2177 nargs = (string, self,) + args
2180 self.raw_to_mode(nkw)
2181 return apply(SCons.Subst.scons_subst_list, nargs, nkw)
2182 def subst_target_source(self, string, *args, **kwargs):
2183 nargs = (string, self,) + args
2186 self.raw_to_mode(nkw)
2187 return apply(SCons.Subst.scons_subst, nargs, nkw)
2188 return _NoSubstitutionProxy(subject)