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
13 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
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__ = "src/engine/SCons/Environment.py 3842 2008/12/20 22:59:52 scons"
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 (or will be) controlled by SCons; users should never
109 # set or override them. This warning can optionally be turned off,
110 # but scons will still ignore the illegal variable names even if it's off.
111 reserved_construction_var_names = [
118 future_reserved_construction_var_names = [
125 def copy_non_reserved_keywords(dict):
126 result = semi_deepcopy(dict)
127 for k in result.keys():
128 if k in reserved_construction_var_names:
129 msg = "Ignoring attempt to set reserved variable `$%s'"
130 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k)
134 def _set_reserved(env, key, value):
135 msg = "Ignoring attempt to set reserved variable `$%s'"
136 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key)
138 def _set_future_reserved(env, key, value):
139 env._dict[key] = value
140 msg = "`$%s' will be reserved in a future release and setting it will become ignored"
141 SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key)
143 def _set_BUILDERS(env, key, value):
149 bd = BuilderDict(kwbd, env)
153 def _del_SCANNERS(env, key):
155 env.scanner_map_delete()
157 def _set_SCANNERS(env, key, value):
158 env._dict[key] = value
159 env.scanner_map_delete()
161 def _delete_duplicates(l, keep_last):
162 """Delete duplicates from a sequence, keeping the first or last."""
165 if keep_last: # reverse in & out, then keep first
169 if not seen.has_key(i):
173 # probably unhashable. Just keep it.
181 # The following is partly based on code in a comment added by Peter
182 # Shannon at the following page (there called the "transplant" class):
184 # ASPN : Python Cookbook : Dynamically added methods to a class
185 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
187 # We had independently been using the idiom as BuilderWrapper, but
188 # factoring out the common parts into this base class, and making
189 # BuilderWrapper a subclass that overrides __call__() to enforce specific
190 # Builder calling conventions, simplified some of our higher-layer code.
194 A generic Wrapper class that associates a method (which can
195 actually be any callable) with an object. As part of creating this
196 MethodWrapper object an attribute with the specified (by default,
197 the name of the supplied method) is added to the underlying object.
198 When that new "method" is called, our __call__() method adds the
199 object as the first argument, simulating the Python behavior of
200 supplying "self" on method calls.
202 We hang on to the name by which the method was added to the underlying
203 base class so that we can provide a method to "clone" ourselves onto
204 a new underlying object being copied (without which we wouldn't need
207 def __init__(self, object, method, name=None):
209 name = method.__name__
213 setattr(self.object, name, self)
215 def __call__(self, *args, **kwargs):
216 nargs = (self.object,) + args
217 return apply(self.method, nargs, kwargs)
219 def clone(self, new_object):
221 Returns an object that re-binds the underlying "method" to
222 the specified new object.
224 return self.__class__(new_object, self.method, self.name)
226 class BuilderWrapper(MethodWrapper):
228 A MethodWrapper subclass that that associates an environment with
231 This mainly exists to wrap the __call__() function so that all calls
232 to Builders can have their argument lists massaged in the same way
233 (treat a lone argument as the source, treat two arguments as target
234 then source, make sure both target and source are lists) without
235 having to have cut-and-paste code to do it.
237 As a bit of obsessive backwards compatibility, we also intercept
238 attempts to get or set the "env" or "builder" attributes, which were
239 the names we used before we put the common functionality into the
240 MethodWrapper base class. We'll keep this around for a while in case
241 people shipped Tool modules that reached into the wrapper (like the
242 Tool/qt.py module does, or did). There shouldn't be a lot attribute
243 fetching or setting on these, so a little extra work shouldn't hurt.
245 def __call__(self, target=None, source=_null, *args, **kw):
249 if not target is None and not SCons.Util.is_List(target):
251 if not source is None and not SCons.Util.is_List(source):
253 return apply(MethodWrapper.__call__, (self, target, source) + args, kw)
256 return '<BuilderWrapper %s>' % repr(self.name)
259 return self.__repr__()
261 def __getattr__(self, name):
264 elif name == 'builder':
267 raise AttributeError, name
269 def __setattr__(self, name, value):
272 elif name == 'builder':
275 self.__dict__[name] = value
277 # This allows a Builder to be executed directly
278 # through the Environment to which it's attached.
279 # In practice, we shouldn't need this, because
280 # builders actually get executed through a Node.
281 # But we do have a unit test for this, and can't
282 # yet rule out that it would be useful in the
283 # future, so leave it for now.
284 #def execute(self, **kw):
285 # kw['env'] = self.env
286 # apply(self.builder.execute, (), kw)
288 class BuilderDict(UserDict):
289 """This is a dictionary-like class used by an Environment to hold
290 the Builders. We need to do this because every time someone changes
291 the Builders in the Environment's BUILDERS dictionary, we must
292 update the Environment's attributes."""
293 def __init__(self, dict, env):
294 # Set self.env before calling the superclass initialization,
295 # because it will end up calling our other methods, which will
296 # need to point the values in this dictionary to self.env.
298 UserDict.__init__(self, dict)
300 def __semi_deepcopy__(self):
301 return self.__class__(self.data, self.env)
303 def __setitem__(self, item, val):
305 method = getattr(self.env, item).method
306 except AttributeError:
309 self.env.RemoveMethod(method)
310 UserDict.__setitem__(self, item, val)
311 BuilderWrapper(self.env, val, item)
313 def __delitem__(self, item):
314 UserDict.__delitem__(self, item)
315 delattr(self.env, item)
317 def update(self, dict):
318 for i, v in dict.items():
319 self.__setitem__(i, v)
323 _is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
325 def is_valid_construction_var(varstr):
326 """Return if the specified string is a legitimate construction
329 return _is_valid_var.match(varstr)
333 class SubstitutionEnvironment:
334 """Base class for different flavors of construction environments.
336 This class contains a minimal set of methods that handle contruction
337 variable expansion and conversion of strings to Nodes, which may or
338 may not be actually useful as a stand-alone class. Which methods
339 ended up in this class is pretty arbitrary right now. They're
340 basically the ones which we've empirically determined are common to
341 the different construction environment subclasses, and most of the
342 others that use or touch the underlying dictionary of construction
345 Eventually, this class should contain all the methods that we
346 determine are necessary for a "minimal" interface to the build engine.
347 A full "native Python" SCons environment has gotten pretty heavyweight
348 with all of the methods and Tools and construction variables we've
349 jammed in there, so it would be nice to have a lighter weight
350 alternative for interfaces that don't need all of the bells and
351 whistles. (At some point, we'll also probably rename this class
352 "Base," since that more reflects what we want this class to become,
353 but because we've released comments that tell people to subclass
354 Environment.Base to create their own flavors of construction
355 environment, we'll save that for a future refactoring when this
356 class actually becomes useful.)
359 if SCons.Memoize.use_memoizer:
360 __metaclass__ = SCons.Memoize.Memoized_Metaclass
362 def __init__(self, **kw):
363 """Initialization of an underlying SubstitutionEnvironment class.
365 if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
366 self.fs = SCons.Node.FS.get_default_fs()
367 self.ans = SCons.Node.Alias.default_ans
368 self.lookup_list = SCons.Node.arg2nodes_lookups
369 self._dict = kw.copy()
371 self.added_methods = []
374 def _init_special(self):
375 """Initial the dispatch tables for special handling of
376 special construction variables."""
377 self._special_del = {}
378 self._special_del['SCANNERS'] = _del_SCANNERS
380 self._special_set = {}
381 for key in reserved_construction_var_names:
382 self._special_set[key] = _set_reserved
383 for key in future_reserved_construction_var_names:
384 self._special_set[key] = _set_future_reserved
385 self._special_set['BUILDERS'] = _set_BUILDERS
386 self._special_set['SCANNERS'] = _set_SCANNERS
388 # Freeze the keys of self._special_set in a list for use by
389 # methods that need to check. (Empirically, list scanning has
390 # gotten better than dict.has_key() in Python 2.5.)
391 self._special_set_keys = self._special_set.keys()
393 def __cmp__(self, other):
394 return cmp(self._dict, other._dict)
396 def __delitem__(self, key):
397 special = self._special_del.get(key)
403 def __getitem__(self, key):
404 return self._dict[key]
406 def __setitem__(self, key, value):
407 # This is heavily used. This implementation is the best we have
408 # according to the timings in bench/env.__setitem__.py.
410 # The "key in self._special_set_keys" test here seems to perform
411 # pretty well for the number of keys we have. A hard-coded
412 # list works a little better in Python 2.5, but that has the
413 # disadvantage of maybe getting out of sync if we ever add more
414 # variable names. Using self._special_set.has_key() works a
415 # little better in Python 2.4, but is worse then this test.
416 # So right now it seems like a good trade-off, but feel free to
417 # revisit this with bench/env.__setitem__.py as needed (and
418 # as newer versions of Python come out).
419 if key in self._special_set_keys:
420 self._special_set[key](self, key, value)
422 # If we already have the entry, then it's obviously a valid
423 # key and we don't need to check. If we do check, using a
424 # global, pre-compiled regular expression directly is more
425 # efficient than calling another function or a method.
426 if not self._dict.has_key(key) \
427 and not _is_valid_var.match(key):
428 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
429 self._dict[key] = value
431 def get(self, key, default=None):
432 "Emulates the get() method of dictionaries."""
433 return self._dict.get(key, default)
435 def has_key(self, key):
436 return self._dict.has_key(key)
438 def __contains__(self, key):
439 return self._dict.__contains__(key)
442 return self._dict.items()
444 def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
445 if node_factory is _null:
446 node_factory = self.fs.File
447 if lookup_list is _null:
448 lookup_list = self.lookup_list
453 args = SCons.Util.flatten(args)
457 if SCons.Util.is_String(v):
459 for l in lookup_list:
464 if SCons.Util.is_String(n):
465 # n = self.subst(n, raw=1, **kw)
467 n = apply(self.subst, (n,), kw)
470 if SCons.Util.is_List(n):
475 # v = node_factory(self.subst(v, raw=1, **kw))
477 v = node_factory(apply(self.subst, (v,), kw))
478 if SCons.Util.is_List(v):
493 def subst(self, string, raw=0, target=None, source=None, conv=None):
494 """Recursively interpolates construction variables from the
495 Environment into the specified string, returning the expanded
496 result. Construction variables are specified by a $ prefix
497 in the string and begin with an initial underscore or
498 alphabetic character followed by any number of underscores
499 or alphanumeric characters. The construction variable names
500 may be surrounded by curly braces to separate the name from
505 lvars['__env__'] = self
506 return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
508 def subst_kw(self, kw, raw=0, target=None, source=None):
510 for k, v in kw.items():
511 k = self.subst(k, raw, target, source)
512 if SCons.Util.is_String(v):
513 v = self.subst(v, raw, target, source)
517 def subst_list(self, string, raw=0, target=None, source=None, conv=None):
518 """Calls through to SCons.Subst.scons_subst_list(). See
519 the documentation for that function."""
522 lvars['__env__'] = self
523 return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
525 def subst_path(self, path, target=None, source=None):
526 """Substitute a path list, turning EntryProxies into Nodes
527 and leaving Nodes (and other objects) as-is."""
529 if not SCons.Util.is_List(path):
533 """This is the "string conversion" routine that we have our
534 substitutions use to return Nodes, not strings. This relies
535 on the fact that an EntryProxy object has a get() method that
536 returns the underlying Node that it wraps, which is a bit of
537 architectural dependence that we might need to break or modify
538 in the future in response to additional requirements."""
541 except AttributeError:
542 obj = SCons.Util.to_String_for_subst(obj)
549 if SCons.Util.is_String(p):
550 p = self.subst(p, target=target, source=source, conv=s)
551 if SCons.Util.is_List(p):
555 # We have an object plus a string, or multiple
556 # objects that we need to smush together. No choice
557 # but to make them into a string.
558 p = string.join(map(SCons.Util.to_String_for_subst, p), '')
564 subst_target_source = subst
566 def backtick(self, command):
569 kw = { 'stdin' : 'devnull',
570 'stdout' : subprocess.PIPE,
571 'stderr' : subprocess.PIPE,
572 'universal_newlines' : True,
574 # if the command is a list, assume it's been quoted
575 # othewise force a shell
576 if not SCons.Util.is_List(command): kw['shell'] = True
577 # run constructed command
578 #TODO(1.5) p = SCons.Action._subproc(self, command, **kw)
579 p = apply(SCons.Action._subproc, (self, command), kw)
580 out,err = p.communicate()
583 sys.stderr.write(err)
585 raise OSError("'%s' exited %d" % (command, status))
588 def AddMethod(self, function, name=None):
590 Adds the specified function as a method of this construction
591 environment with the specified name. If the name is omitted,
592 the default name is the name of the function itself.
594 method = MethodWrapper(self, function, name)
595 self.added_methods.append(method)
597 def RemoveMethod(self, function):
599 Removes the specified function's MethodWrapper from the
600 added_methods list, so we don't re-bind it when making a clone.
602 is_not_func = lambda dm, f=function: not dm.method is f
603 self.added_methods = filter(is_not_func, self.added_methods)
605 def Override(self, overrides):
607 Produce a modified environment whose variables are overriden by
608 the overrides dictionaries. "overrides" is a dictionary that
609 will override the variables of this environment.
611 This function is much more efficient than Clone() or creating
612 a new Environment because it doesn't copy the construction
613 environment dictionary, it just wraps the underlying construction
614 environment, and doesn't even create a wrapper object if there
617 if not overrides: return self
618 o = copy_non_reserved_keywords(overrides)
619 if not o: return self
622 for key, value in o.items():
623 if key == 'parse_flags':
626 overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
627 env = OverrideEnvironment(self, overrides)
628 if merges: env.MergeFlags(merges)
631 def ParseFlags(self, *flags):
633 Parse the set of flags and return a dict with the flags placed
634 in the appropriate entry. The flags are treated as a typical
635 set of command-line flags for a GNU-like toolchain and used to
636 populate the entries in the dict immediately below. If one of
637 the flag strings begins with a bang (exclamation mark), it is
638 assumed to be a command and the rest of the string is executed;
639 the result of that evaluation is then added to the dict.
642 'ASFLAGS' : SCons.Util.CLVar(''),
643 'CFLAGS' : SCons.Util.CLVar(''),
644 'CCFLAGS' : SCons.Util.CLVar(''),
646 'CPPFLAGS' : SCons.Util.CLVar(''),
648 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
649 'FRAMEWORKS' : SCons.Util.CLVar(''),
652 'LINKFLAGS' : SCons.Util.CLVar(''),
656 # The use of the "me" parameter to provide our own name for
657 # recursion is an egregious hack to support Python 2.1 and before.
658 def do_parse(arg, me, self = self, dict = dict):
659 # if arg is a sequence, recurse with each element
663 if not SCons.Util.is_String(arg):
664 for t in arg: me(t, me)
667 # if arg is a command, execute it
669 arg = self.backtick(arg[1:])
671 # utility function to deal with -D option
672 def append_define(name, dict = dict):
673 t = string.split(name, '=')
675 dict['CPPDEFINES'].append(name)
677 dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
679 # Loop through the flags and add them to the appropriate option.
680 # This tries to strike a balance between checking for all possible
681 # flags and keeping the logic to a finite size, so it doesn't
682 # check for some that don't occur often. It particular, if the
683 # flag is not known to occur in a config script and there's a way
684 # of passing the flag to the right place (by wrapping it in a -W
685 # flag, for example) we don't check for it. Note that most
686 # preprocessor options are not handled, since unhandled options
687 # are placed in CCFLAGS, so unless the preprocessor is invoked
688 # separately, these flags will still get to the preprocessor.
689 # Other options not currently handled:
690 # -iqoutedir (preprocessor search path)
691 # -u symbol (linker undefined symbol)
692 # -s (linker strip files)
693 # -static* (linker static binding)
694 # -shared* (linker dynamic binding)
695 # -symbolic (linker global binding)
696 # -R dir (deprecated linker rpath)
697 # IBM compilers may also accept -qframeworkdir=foo
699 params = shlex.split(arg)
700 append_next_arg_to = None # for multi-word args
702 if append_next_arg_to:
703 if append_next_arg_to == 'CPPDEFINES':
705 elif append_next_arg_to == '-include':
706 t = ('-include', self.fs.File(arg))
707 dict['CCFLAGS'].append(t)
708 elif append_next_arg_to == '-isysroot':
709 t = ('-isysroot', arg)
710 dict['CCFLAGS'].append(t)
711 dict['LINKFLAGS'].append(t)
712 elif append_next_arg_to == '-arch':
714 dict['CCFLAGS'].append(t)
715 dict['LINKFLAGS'].append(t)
717 dict[append_next_arg_to].append(arg)
718 append_next_arg_to = None
719 elif not arg[0] in ['-', '+']:
720 dict['LIBS'].append(self.fs.File(arg))
721 elif arg[:2] == '-L':
723 dict['LIBPATH'].append(arg[2:])
725 append_next_arg_to = 'LIBPATH'
726 elif arg[:2] == '-l':
728 dict['LIBS'].append(arg[2:])
730 append_next_arg_to = 'LIBS'
731 elif arg[:2] == '-I':
733 dict['CPPPATH'].append(arg[2:])
735 append_next_arg_to = 'CPPPATH'
736 elif arg[:4] == '-Wa,':
737 dict['ASFLAGS'].append(arg[4:])
738 dict['CCFLAGS'].append(arg)
739 elif arg[:4] == '-Wl,':
740 if arg[:11] == '-Wl,-rpath=':
741 dict['RPATH'].append(arg[11:])
742 elif arg[:7] == '-Wl,-R,':
743 dict['RPATH'].append(arg[7:])
744 elif arg[:6] == '-Wl,-R':
745 dict['RPATH'].append(arg[6:])
747 dict['LINKFLAGS'].append(arg)
748 elif arg[:4] == '-Wp,':
749 dict['CPPFLAGS'].append(arg)
750 elif arg[:2] == '-D':
752 append_define(arg[2:])
754 append_next_arg_to = 'CPPDEFINES'
755 elif arg == '-framework':
756 append_next_arg_to = 'FRAMEWORKS'
757 elif arg[:14] == '-frameworkdir=':
758 dict['FRAMEWORKPATH'].append(arg[14:])
759 elif arg[:2] == '-F':
761 dict['FRAMEWORKPATH'].append(arg[2:])
763 append_next_arg_to = 'FRAMEWORKPATH'
764 elif arg == '-mno-cygwin':
765 dict['CCFLAGS'].append(arg)
766 dict['LINKFLAGS'].append(arg)
767 elif arg == '-mwindows':
768 dict['LINKFLAGS'].append(arg)
769 elif arg == '-pthread':
770 dict['CCFLAGS'].append(arg)
771 dict['LINKFLAGS'].append(arg)
772 elif arg[:5] == '-std=':
773 dict['CFLAGS'].append(arg) # C only
775 dict['CCFLAGS'].append(arg)
776 dict['LINKFLAGS'].append(arg)
777 elif arg in ['-include', '-isysroot', '-arch']:
778 append_next_arg_to = arg
780 dict['CCFLAGS'].append(arg)
783 do_parse(arg, do_parse)
786 def MergeFlags(self, args, unique=1, dict=None):
788 Merge the dict in args into the construction variables of this
789 env, or the passed-in dict. If args is not a dict, it is
790 converted into a dict using ParseFlags. If unique is not set,
791 the flags are appended rather than merged.
796 if not SCons.Util.is_Dict(args):
797 args = self.ParseFlags(args)
799 apply(self.Append, (), args)
801 for key, value in args.items():
812 # Add orig and value. The logic here was lifted from
813 # part of env.Append() (see there for a lot of comments
814 # about the order in which things are tried) and is
815 # used mainly to handle coercion of strings to CLVar to
816 # "do the right thing" given (e.g.) an original CCFLAGS
817 # string variable like '-pipe -Wall'.
820 except (KeyError, TypeError):
822 add_to_orig = orig.append
823 except AttributeError:
824 value.insert(0, orig)
829 if key[-4:] == 'PATH':
830 ### keep left-most occurence
835 ### keep right-most occurence
843 # def MergeShellPaths(self, args, prepend=1):
845 # Merge the dict in args into the shell environment in env['ENV'].
846 # Shell path elements are appended or prepended according to prepend.
848 # Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
850 # Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'})
851 # prepends /usr/local/lib to env['ENV']['LIBPATH'].
854 # for pathname, pathval in args.items():
858 # apply(self.PrependENVPath, (pathname, pathval))
860 # apply(self.AppendENVPath, (pathname, pathval))
863 # Used by the FindSourceFiles() method, below.
864 # Stuck here for support of pre-2.2 Python versions.
865 def build_source(ss, result):
867 if isinstance(s, SCons.Node.FS.Dir):
868 build_source(s.all_children(), result)
869 elif s.has_builder():
870 build_source(s.sources, result)
871 elif isinstance(s.disambiguate(), SCons.Node.FS.File):
874 def default_decide_source(dependency, target, prev_ni):
875 f = SCons.Defaults.DefaultEnvironment().decide_source
876 return f(dependency, target, prev_ni)
878 def default_decide_target(dependency, target, prev_ni):
879 f = SCons.Defaults.DefaultEnvironment().decide_target
880 return f(dependency, target, prev_ni)
882 def default_copy_from_cache(src, dst):
883 f = SCons.Defaults.DefaultEnvironment().copy_from_cache
886 class Base(SubstitutionEnvironment):
887 """Base class for "real" construction Environments. These are the
888 primary objects used to communicate dependency and construction
889 information to the build engine.
891 Keyword arguments supplied when the construction Environment
892 is created are construction variables used to initialize the
896 if SCons.Memoize.use_memoizer:
897 __metaclass__ = SCons.Memoize.Memoized_Metaclass
899 memoizer_counters = []
901 #######################################################################
902 # This is THE class for interacting with the SCons build engine,
903 # and it contains a lot of stuff, so we're going to try to keep this
904 # a little organized by grouping the methods.
905 #######################################################################
907 #######################################################################
908 # Methods that make an Environment act like a dictionary. These have
909 # the expected standard names for Python mapping objects. Note that
910 # we don't actually make an Environment a subclass of UserDict for
911 # performance reasons. Note also that we only supply methods for
912 # dictionary functionality that we actually need and use.
913 #######################################################################
923 Initialization of a basic SCons construction environment,
924 including setting up special construction variables like BUILDER,
925 PLATFORM, etc., and searching for and applying available Tools.
927 Note that we do *not* call the underlying base class
928 (SubsitutionEnvironment) initialization, because we need to
929 initialize things in a very specific order that doesn't work
930 with the much simpler base class initialization.
932 if __debug__: logInstanceCreation(self, 'Environment.Base')
934 self.fs = SCons.Node.FS.get_default_fs()
935 self.ans = SCons.Node.Alias.default_ans
936 self.lookup_list = SCons.Node.arg2nodes_lookups
937 self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment)
939 self.added_methods = []
941 # We don't use AddMethod, or define these as methods in this
942 # class, because we *don't* want these functions to be bound
943 # methods. They need to operate independently so that the
944 # settings will work properly regardless of whether a given
945 # target ends up being built with a Base environment or an
946 # OverrideEnvironment or what have you.
947 self.decide_target = default_decide_target
948 self.decide_source = default_decide_source
950 self.copy_from_cache = default_copy_from_cache
952 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
955 platform = self._dict.get('PLATFORM', None)
957 platform = SCons.Platform.Platform()
958 if SCons.Util.is_String(platform):
959 platform = SCons.Platform.Platform(platform)
960 self._dict['PLATFORM'] = str(platform)
963 # Apply the passed-in and customizable variables to the
964 # environment before calling the tools, because they may use
965 # some of them during initialization.
966 if kw.has_key('options'):
967 # Backwards compatibility: they may stll be using the
968 # old "options" keyword.
969 variables = kw['options']
971 apply(self.Replace, (), kw)
974 keys = keys + variables.keys()
975 variables.Update(self)
980 save[k] = self._dict[k]
982 # No value may have been set if they tried to pass in a
983 # reserved variable name like TARGETS.
986 SCons.Tool.Initializers(self)
989 tools = self._dict.get('TOOLS', None)
992 apply_tools(self, tools, toolpath)
994 # Now restore the passed-in and customized variables
995 # to the environment, since the values the user set explicitly
996 # should override any values set by the tools.
997 for key, val in save.items():
998 self._dict[key] = val
1000 # Finally, apply any flags to be merged in
1001 if parse_flags: self.MergeFlags(parse_flags)
1003 #######################################################################
1004 # Utility methods that are primarily for internal use by SCons.
1005 # These begin with lower-case letters.
1006 #######################################################################
1008 def get_builder(self, name):
1009 """Fetch the builder with the specified name from the environment.
1012 return self._dict['BUILDERS'][name]
1016 def get_CacheDir(self):
1018 path = self._CacheDir_path
1019 except AttributeError:
1020 path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
1022 if path == self._last_CacheDir_path:
1023 return self._last_CacheDir
1024 except AttributeError:
1026 cd = SCons.CacheDir.CacheDir(path)
1027 self._last_CacheDir_path = path
1028 self._last_CacheDir = cd
1031 def get_factory(self, factory, default='File'):
1032 """Return a factory function for creating Nodes for this
1033 construction environment.
1037 is_node = issubclass(factory, SCons.Node.Node)
1039 # The specified factory isn't a Node itself--it's
1040 # most likely None, or possibly a callable.
1044 # The specified factory is a Node (sub)class. Try to
1045 # return the FS method that corresponds to the Node's
1046 # name--that is, we return self.fs.Dir if they want a Dir,
1047 # self.fs.File for a File, etc.
1048 try: name = factory.__name__
1049 except AttributeError: pass
1050 else: factory = None
1052 # They passed us None, or we picked up a name from a specified
1053 # class, so return the FS method. (Note that we *don't*
1054 # use our own self.{Dir,File} methods because that would
1055 # cause env.subst() to be called twice on the file name,
1056 # interfering with files that have $$ in them.)
1057 factory = getattr(self.fs, name)
1060 memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
1064 return self._memo['_gsm']
1071 scanners = self._dict['SCANNERS']
1075 # Reverse the scanner list so that, if multiple scanners
1076 # claim they can scan the same suffix, earlier scanners
1077 # in the list will overwrite later scanners, so that
1078 # the result looks like a "first match" to the user.
1079 if not SCons.Util.is_List(scanners):
1080 scanners = [scanners]
1082 scanners = scanners[:] # copy so reverse() doesn't mod original
1084 for scanner in scanners:
1085 for k in scanner.get_skeys(self):
1088 self._memo['_gsm'] = result
1092 def get_scanner(self, skey):
1093 """Find the appropriate scanner given a key (usually a file suffix).
1095 return self._gsm().get(skey)
1097 def scanner_map_delete(self, kw=None):
1098 """Delete the cached scanner map (if we need to).
1101 del self._memo['_gsm']
1105 def _update(self, dict):
1106 """Update an environment's values directly, bypassing the normal
1107 checks that occur when users try to set items.
1109 self._dict.update(dict)
1111 def get_src_sig_type(self):
1113 return self.src_sig_type
1114 except AttributeError:
1115 t = SCons.Defaults.DefaultEnvironment().src_sig_type
1116 self.src_sig_type = t
1119 def get_tgt_sig_type(self):
1121 return self.tgt_sig_type
1122 except AttributeError:
1123 t = SCons.Defaults.DefaultEnvironment().tgt_sig_type
1124 self.tgt_sig_type = t
1127 #######################################################################
1128 # Public methods for manipulating an Environment. These begin with
1129 # upper-case letters. The essential characteristic of methods in
1130 # this section is that they do *not* have corresponding same-named
1131 # global functions. For example, a stand-alone Append() function
1132 # makes no sense, because Append() is all about appending values to
1133 # an Environment's construction variables.
1134 #######################################################################
1136 def Append(self, **kw):
1137 """Append values to existing construction variables
1140 kw = copy_non_reserved_keywords(kw)
1141 for key, val in kw.items():
1142 # It would be easier on the eyes to write this using
1143 # "continue" statements whenever we finish processing an item,
1144 # but Python 1.5.2 apparently doesn't let you use "continue"
1145 # within try:-except: blocks, so we have to nest our code.
1147 orig = self._dict[key]
1149 # No existing variable in the environment, so just set
1150 # it to the new value.
1151 self._dict[key] = val
1154 # Check if the original looks like a dictionary.
1155 # If it is, we can't just try adding the value because
1156 # dictionaries don't have __add__() methods, and
1157 # things like UserList will incorrectly coerce the
1158 # original dict to a list (which we don't want).
1159 update_dict = orig.update
1160 except AttributeError:
1162 # Most straightforward: just try to add them
1163 # together. This will work in most cases, when the
1164 # original and new values are of compatible types.
1165 self._dict[key] = orig + val
1166 except (KeyError, TypeError):
1168 # Check if the original is a list.
1169 add_to_orig = orig.append
1170 except AttributeError:
1171 # The original isn't a list, but the new
1172 # value is (by process of elimination),
1173 # so insert the original in the new value
1174 # (if there's one to insert) and replace
1175 # the variable with it.
1178 self._dict[key] = val
1180 # The original is a list, so append the new
1181 # value to it (if there's a value to append).
1185 # The original looks like a dictionary, so update it
1186 # based on what we think the value looks like.
1187 if SCons.Util.is_List(val):
1193 except (AttributeError, TypeError, ValueError):
1194 if SCons.Util.is_Dict(val):
1195 for k, v in val.items():
1199 self.scanner_map_delete(kw)
1201 def AppendENVPath(self, name, newpath, envname = 'ENV',
1202 sep = os.pathsep, delete_existing=1):
1203 """Append path elements to the path 'name' in the 'ENV'
1204 dictionary for this environment. Will only add any particular
1205 path once, and will normpath and normcase all paths to help
1206 assure this. This can also handle the case where the env
1207 variable is a list instead of a string.
1209 If delete_existing is 0, a newpath which is already in the path
1210 will not be moved to the end (it will be left where it is).
1214 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1215 orig = self._dict[envname][name]
1217 nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing)
1219 if not self._dict.has_key(envname):
1220 self._dict[envname] = {}
1222 self._dict[envname][name] = nv
1224 def AppendUnique(self, delete_existing=0, **kw):
1225 """Append values to existing construction variables
1226 in an Environment, if they're not already there.
1227 If delete_existing is 1, removes existing values first, so
1230 kw = copy_non_reserved_keywords(kw)
1231 for key, val in kw.items():
1232 if SCons.Util.is_List(val):
1233 val = _delete_duplicates(val, delete_existing)
1234 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1235 self._dict[key] = val
1236 elif SCons.Util.is_Dict(self._dict[key]) and \
1237 SCons.Util.is_Dict(val):
1238 self._dict[key].update(val)
1239 elif SCons.Util.is_List(val):
1240 dk = self._dict[key]
1241 if not SCons.Util.is_List(dk):
1244 dk = filter(lambda x, val=val: x not in val, dk)
1246 val = filter(lambda x, dk=dk: x not in dk, val)
1247 self._dict[key] = dk + val
1249 dk = self._dict[key]
1250 if SCons.Util.is_List(dk):
1251 # By elimination, val is not a list. Since dk is a
1252 # list, wrap val in a list first.
1254 dk = filter(lambda x, val=val: x not in val, dk)
1255 self._dict[key] = dk + [val]
1258 self._dict[key] = dk + [val]
1261 dk = filter(lambda x, val=val: x not in val, dk)
1262 self._dict[key] = dk + val
1263 self.scanner_map_delete(kw)
1265 def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
1266 """Return a copy of a construction Environment. The
1267 copy is like a Python "deep copy"--that is, independent
1268 copies are made recursively of each objects--except that
1269 a reference is copied when an object is not deep-copyable
1270 (like a function). There are no references to any mutable
1271 objects in the original Environment.
1273 clone = copy.copy(self)
1274 clone._dict = semi_deepcopy(self._dict)
1277 cbd = clone._dict['BUILDERS']
1281 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
1283 # Check the methods added via AddMethod() and re-bind them to
1284 # the cloned environment. Only do this if the attribute hasn't
1285 # been overwritten by the user explicitly and still points to
1287 clone.added_methods = []
1288 for mw in self.added_methods:
1289 if mw == getattr(self, mw.name):
1290 clone.added_methods.append(mw.clone(clone))
1294 # Apply passed-in variables before the tools
1295 # so the tools can use the new variables
1296 kw = copy_non_reserved_keywords(kw)
1298 for key, value in kw.items():
1299 new[key] = SCons.Subst.scons_subst_once(value, self, key)
1300 apply(clone.Replace, (), new)
1302 apply_tools(clone, tools, toolpath)
1304 # apply them again in case the tools overwrote them
1305 apply(clone.Replace, (), new)
1307 # Finally, apply any flags to be merged in
1308 if parse_flags: clone.MergeFlags(parse_flags)
1310 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
1313 def Copy(self, *args, **kw):
1314 global _warn_copy_deprecated
1315 if _warn_copy_deprecated:
1316 msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
1317 SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
1318 _warn_copy_deprecated = False
1319 return apply(self.Clone, args, kw)
1321 def _changed_build(self, dependency, target, prev_ni):
1322 if dependency.changed_state(target, prev_ni):
1324 return self.decide_source(dependency, target, prev_ni)
1326 def _changed_content(self, dependency, target, prev_ni):
1327 return dependency.changed_content(target, prev_ni)
1329 def _changed_source(self, dependency, target, prev_ni):
1330 target_env = dependency.get_build_env()
1331 type = target_env.get_tgt_sig_type()
1332 if type == 'source':
1333 return target_env.decide_source(dependency, target, prev_ni)
1335 return target_env.decide_target(dependency, target, prev_ni)
1337 def _changed_timestamp_then_content(self, dependency, target, prev_ni):
1338 return dependency.changed_timestamp_then_content(target, prev_ni)
1340 def _changed_timestamp_newer(self, dependency, target, prev_ni):
1341 return dependency.changed_timestamp_newer(target, prev_ni)
1343 def _changed_timestamp_match(self, dependency, target, prev_ni):
1344 return dependency.changed_timestamp_match(target, prev_ni)
1346 def _copy_from_cache(self, src, dst):
1347 return self.fs.copy(src, dst)
1349 def _copy2_from_cache(self, src, dst):
1350 return self.fs.copy2(src, dst)
1352 def Decider(self, function):
1353 copy_function = self._copy2_from_cache
1354 if function in ('MD5', 'content'):
1355 if not SCons.Util.md5:
1356 raise UserError, "MD5 signatures are not available in this version of Python."
1357 function = self._changed_content
1358 elif function == 'MD5-timestamp':
1359 function = self._changed_timestamp_then_content
1360 elif function in ('timestamp-newer', 'make'):
1361 function = self._changed_timestamp_newer
1362 copy_function = self._copy_from_cache
1363 elif function == 'timestamp-match':
1364 function = self._changed_timestamp_match
1365 elif not callable(function):
1366 raise UserError, "Unknown Decider value %s" % repr(function)
1368 # We don't use AddMethod because we don't want to turn the
1369 # function, which only expects three arguments, into a bound
1370 # method, which would add self as an initial, fourth argument.
1371 self.decide_target = function
1372 self.decide_source = function
1374 self.copy_from_cache = copy_function
1376 def Detect(self, progs):
1377 """Return the first available program in progs.
1379 if not SCons.Util.is_List(progs):
1382 path = self.WhereIs(prog)
1383 if path: return prog
1386 def Dictionary(self, *args):
1389 dlist = map(lambda x, s=self: s._dict[x], args)
1394 def Dump(self, key = None):
1396 Using the standard Python pretty printer, dump the contents of the
1397 scons build environment to stdout.
1399 If the key passed in is anything other than None, then that will
1400 be used as an index into the build environment dictionary and
1401 whatever is found there will be fed into the pretty printer. Note
1402 that this key is case sensitive.
1405 pp = pprint.PrettyPrinter(indent=2)
1407 dict = self.Dictionary(key)
1409 dict = self.Dictionary()
1410 return pp.pformat(dict)
1412 def FindIxes(self, paths, prefix, suffix):
1414 Search a list of paths for something that matches the prefix and suffix.
1416 paths - the list of paths or nodes.
1417 prefix - construction variable for the prefix.
1418 suffix - construction variable for the suffix.
1421 suffix = self.subst('$'+suffix)
1422 prefix = self.subst('$'+prefix)
1425 dir,name = os.path.split(str(path))
1426 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
1429 def ParseConfig(self, command, function=None, unique=1):
1431 Use the specified function to parse the output of the command
1432 in order to modify the current environment. The 'command' can
1433 be a string or a list of strings representing a command and
1434 its arguments. 'Function' is an optional argument that takes
1435 the environment, the output of the command, and the unique flag.
1436 If no function is specified, MergeFlags, which treats the output
1437 as the result of a typical 'X-config' command (i.e. gtk-config),
1438 will merge the output into the appropriate variables.
1440 if function is None:
1441 def parse_conf(env, cmd, unique=unique):
1442 return env.MergeFlags(cmd, unique)
1443 function = parse_conf
1444 if SCons.Util.is_List(command):
1445 command = string.join(command)
1446 command = self.subst(command)
1447 return function(self, self.backtick(command))
1449 def ParseDepends(self, filename, must_exist=None, only_one=0):
1451 Parse a mkdep-style file for explicit dependencies. This is
1452 completely abusable, and should be unnecessary in the "normal"
1453 case of proper SCons configuration, but it may help make
1454 the transition from a Make hierarchy easier for some people
1455 to swallow. It can also be genuinely useful when using a tool
1456 that can write a .d file, but for which writing a scanner would
1459 filename = self.subst(filename)
1461 fp = open(filename, 'r')
1466 lines = SCons.Util.LogicalLines(fp).readlines()
1467 lines = filter(lambda l: l[0] != '#', lines)
1471 target, depends = string.split(line, ':', 1)
1472 except (AttributeError, TypeError, ValueError):
1473 # Python 1.5.2 throws TypeError if line isn't a string,
1474 # Python 2.x throws AttributeError because it tries
1475 # to call line.split(). Either can throw ValueError
1476 # if the line doesn't split into two or more elements.
1479 tdlist.append((string.split(target), string.split(depends)))
1481 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
1482 if len(targets) > 1:
1483 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
1484 for target, depends in tdlist:
1485 self.Depends(target, depends)
1487 def Platform(self, platform):
1488 platform = self.subst(platform)
1489 return SCons.Platform.Platform(platform)(self)
1491 def Prepend(self, **kw):
1492 """Prepend values to existing construction variables
1495 kw = copy_non_reserved_keywords(kw)
1496 for key, val in kw.items():
1497 # It would be easier on the eyes to write this using
1498 # "continue" statements whenever we finish processing an item,
1499 # but Python 1.5.2 apparently doesn't let you use "continue"
1500 # within try:-except: blocks, so we have to nest our code.
1502 orig = self._dict[key]
1504 # No existing variable in the environment, so just set
1505 # it to the new value.
1506 self._dict[key] = val
1509 # Check if the original looks like a dictionary.
1510 # If it is, we can't just try adding the value because
1511 # dictionaries don't have __add__() methods, and
1512 # things like UserList will incorrectly coerce the
1513 # original dict to a list (which we don't want).
1514 update_dict = orig.update
1515 except AttributeError:
1517 # Most straightforward: just try to add them
1518 # together. This will work in most cases, when the
1519 # original and new values are of compatible types.
1520 self._dict[key] = val + orig
1521 except (KeyError, TypeError):
1523 # Check if the added value is a list.
1524 add_to_val = val.append
1525 except AttributeError:
1526 # The added value isn't a list, but the
1527 # original is (by process of elimination),
1528 # so insert the the new value in the original
1529 # (if there's one to insert).
1533 # The added value is a list, so append
1534 # the original to it (if there's a value
1538 self._dict[key] = val
1540 # The original looks like a dictionary, so update it
1541 # based on what we think the value looks like.
1542 if SCons.Util.is_List(val):
1548 except (AttributeError, TypeError, ValueError):
1549 if SCons.Util.is_Dict(val):
1550 for k, v in val.items():
1554 self.scanner_map_delete(kw)
1556 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep,
1558 """Prepend path elements to the path 'name' in the 'ENV'
1559 dictionary for this environment. Will only add any particular
1560 path once, and will normpath and normcase all paths to help
1561 assure this. This can also handle the case where the env
1562 variable is a list instead of a string.
1564 If delete_existing is 0, a newpath which is already in the path
1565 will not be moved to the front (it will be left where it is).
1569 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1570 orig = self._dict[envname][name]
1572 nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing)
1574 if not self._dict.has_key(envname):
1575 self._dict[envname] = {}
1577 self._dict[envname][name] = nv
1579 def PrependUnique(self, delete_existing=0, **kw):
1580 """Prepend values to existing construction variables
1581 in an Environment, if they're not already there.
1582 If delete_existing is 1, removes existing values first, so
1583 values move to front.
1585 kw = copy_non_reserved_keywords(kw)
1586 for key, val in kw.items():
1587 if SCons.Util.is_List(val):
1588 val = _delete_duplicates(val, not delete_existing)
1589 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1590 self._dict[key] = val
1591 elif SCons.Util.is_Dict(self._dict[key]) and \
1592 SCons.Util.is_Dict(val):
1593 self._dict[key].update(val)
1594 elif SCons.Util.is_List(val):
1595 dk = self._dict[key]
1596 if not SCons.Util.is_List(dk):
1599 dk = filter(lambda x, val=val: x not in val, dk)
1601 val = filter(lambda x, dk=dk: x not in dk, val)
1602 self._dict[key] = val + dk
1604 dk = self._dict[key]
1605 if SCons.Util.is_List(dk):
1606 # By elimination, val is not a list. Since dk is a
1607 # list, wrap val in a list first.
1609 dk = filter(lambda x, val=val: x not in val, dk)
1610 self._dict[key] = [val] + dk
1613 self._dict[key] = [val] + dk
1616 dk = filter(lambda x, val=val: x not in val, dk)
1617 self._dict[key] = val + dk
1618 self.scanner_map_delete(kw)
1620 def Replace(self, **kw):
1621 """Replace existing construction variables in an Environment
1622 with new construction variables and/or values.
1625 kwbd = kw['BUILDERS']
1629 kwbd = semi_deepcopy(kwbd)
1631 self.__setitem__('BUILDERS', kwbd)
1632 kw = copy_non_reserved_keywords(kw)
1633 self._update(semi_deepcopy(kw))
1634 self.scanner_map_delete(kw)
1636 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1638 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1640 env - Environment used to interpolate variables.
1641 path - the path that will be modified.
1642 old_prefix - construction variable for the old prefix.
1643 old_suffix - construction variable for the old suffix.
1644 new_prefix - construction variable for the new prefix.
1645 new_suffix - construction variable for the new suffix.
1647 old_prefix = self.subst('$'+old_prefix)
1648 old_suffix = self.subst('$'+old_suffix)
1650 new_prefix = self.subst('$'+new_prefix)
1651 new_suffix = self.subst('$'+new_suffix)
1653 dir,name = os.path.split(str(path))
1654 if name[:len(old_prefix)] == old_prefix:
1655 name = name[len(old_prefix):]
1656 if name[-len(old_suffix):] == old_suffix:
1657 name = name[:-len(old_suffix)]
1658 return os.path.join(dir, new_prefix+name+new_suffix)
1660 def SetDefault(self, **kw):
1662 if self._dict.has_key(k):
1664 apply(self.Replace, (), kw)
1666 def _find_toolpath_dir(self, tp):
1667 return self.fs.Dir(self.subst(tp)).srcnode().abspath
1669 def Tool(self, tool, toolpath=None, **kw):
1670 if SCons.Util.is_String(tool):
1671 tool = self.subst(tool)
1672 if toolpath is None:
1673 toolpath = self.get('toolpath', [])
1674 toolpath = map(self._find_toolpath_dir, toolpath)
1675 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
1678 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1679 """Find prog in the path.
1683 path = self['ENV']['PATH']
1686 elif SCons.Util.is_String(path):
1687 path = self.subst(path)
1690 pathext = self['ENV']['PATHEXT']
1693 elif SCons.Util.is_String(pathext):
1694 pathext = self.subst(pathext)
1695 prog = self.subst(prog)
1696 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1697 if path: return path
1700 #######################################################################
1701 # Public methods for doing real "SCons stuff" (manipulating
1702 # dependencies, setting attributes on targets, etc.). These begin
1703 # with upper-case letters. The essential characteristic of methods
1704 # in this section is that they all *should* have corresponding
1705 # same-named global functions.
1706 #######################################################################
1708 def Action(self, *args, **kw):
1709 def subst_string(a, self=self):
1710 if SCons.Util.is_String(a):
1713 nargs = map(subst_string, args)
1714 nkw = self.subst_kw(kw)
1715 return apply(SCons.Action.Action, nargs, nkw)
1717 def AddPreAction(self, files, action):
1718 nodes = self.arg2nodes(files, self.fs.Entry)
1719 action = SCons.Action.Action(action)
1721 for executor in map(lambda n: n.get_executor(), nodes):
1723 for executor in uniq.keys():
1724 executor.add_pre_action(action)
1727 def AddPostAction(self, files, action):
1728 nodes = self.arg2nodes(files, self.fs.Entry)
1729 action = SCons.Action.Action(action)
1731 for executor in map(lambda n: n.get_executor(), nodes):
1733 for executor in uniq.keys():
1734 executor.add_post_action(action)
1737 def Alias(self, target, source=[], action=None, **kw):
1738 tlist = self.arg2nodes(target, self.ans.Alias)
1739 if not SCons.Util.is_List(source):
1741 source = filter(None, source)
1745 # There are no source files and no action, so just
1746 # return a target list of classic Alias Nodes, without
1747 # any builder. The externally visible effect is that
1748 # this will make the wrapping Script.BuildTask class
1749 # say that there's "Nothing to be done" for this Alias,
1750 # instead of that it's "up to date."
1753 # No action, but there are sources. Re-call all the target
1754 # builders to add the sources to each target.
1757 bld = t.get_builder(AliasBuilder)
1758 result.extend(bld(self, t, source))
1761 nkw = self.subst_kw(kw)
1763 'action' : SCons.Action.Action(action),
1764 'source_factory' : self.fs.Entry,
1766 'is_explicit' : None,
1768 bld = apply(SCons.Builder.Builder, (), nkw)
1770 # Apply the Builder separately to each target so that the Aliases
1771 # stay separate. If we did one "normal" Builder call with the
1772 # whole target list, then all of the target Aliases would be
1773 # associated under a single Executor.
1776 # Calling the convert() method will cause a new Executor to be
1777 # created from scratch, so we have to explicitly initialize
1778 # it with the target's existing sources, plus our new ones,
1779 # so nothing gets lost.
1781 if b is None or b is AliasBuilder:
1784 nkw['action'] = b.action + action
1785 b = apply(SCons.Builder.Builder, (), nkw)
1787 result.extend(b(self, t, t.sources + source))
1790 def AlwaysBuild(self, *targets):
1793 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1795 t.set_always_build()
1798 def BuildDir(self, *args, **kw):
1799 if kw.has_key('build_dir'):
1800 kw['variant_dir'] = kw['build_dir']
1802 return apply(self.VariantDir, args, kw)
1804 def Builder(self, **kw):
1805 nkw = self.subst_kw(kw)
1806 return apply(SCons.Builder.Builder, [], nkw)
1808 def CacheDir(self, path):
1809 import SCons.CacheDir
1810 if not path is None:
1811 path = self.subst(path)
1812 self._CacheDir_path = path
1814 def Clean(self, targets, files):
1816 tlist = self.arg2nodes(targets, self.fs.Entry)
1817 flist = self.arg2nodes(files, self.fs.Entry)
1820 CleanTargets[t].extend(flist)
1822 CleanTargets[t] = flist
1824 def Configure(self, *args, **kw):
1827 nargs = nargs + self.subst_list(args)[0]
1828 nkw = self.subst_kw(kw)
1829 nkw['_depth'] = kw.get('_depth', 0) + 1
1831 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1834 return apply(SCons.SConf.SConf, nargs, nkw)
1836 def Command(self, target, source, action, **kw):
1837 """Builds the supplied target files from the supplied
1838 source files using the supplied action. Action may
1839 be any type that the Builder constructor will accept
1843 'target_factory' : self.fs.Entry,
1844 'source_factory' : self.fs.Entry,
1846 try: bkw['source_scanner'] = kw['source_scanner']
1847 except KeyError: pass
1848 else: del kw['source_scanner']
1849 bld = apply(SCons.Builder.Builder, (), bkw)
1850 return apply(bld, (self, target, source), kw)
1852 def Depends(self, target, dependency):
1853 """Explicity specify that 'target's depend on 'dependency'."""
1854 tlist = self.arg2nodes(target, self.fs.Entry)
1855 dlist = self.arg2nodes(dependency, self.fs.Entry)
1857 t.add_dependency(dlist)
1860 def Dir(self, name, *args, **kw):
1863 s = self.subst(name)
1864 if SCons.Util.is_Sequence(s):
1867 result.append(apply(self.fs.Dir, (e,) + args, kw))
1869 return apply(self.fs.Dir, (s,) + args, kw)
1871 def NoClean(self, *targets):
1872 """Tags a target so that it will not be cleaned by -c"""
1875 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1880 def NoCache(self, *targets):
1881 """Tags a target so that it will not be cached"""
1884 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1889 def Entry(self, name, *args, **kw):
1892 s = self.subst(name)
1893 if SCons.Util.is_Sequence(s):
1896 result.append(apply(self.fs.Entry, (e,) + args, kw))
1898 return apply(self.fs.Entry, (s,) + args, kw)
1900 def Environment(self, **kw):
1901 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1903 def Execute(self, action, *args, **kw):
1904 """Directly execute an action through an Environment
1906 action = apply(self.Action, (action,) + args, kw)
1907 result = action([], [], self)
1908 if isinstance(result, SCons.Errors.BuildError):
1909 errstr = result.errstr
1911 errstr = result.filename + ': ' + errstr
1912 sys.stderr.write("scons: *** %s\n" % errstr)
1913 return result.status
1917 def File(self, name, *args, **kw):
1920 s = self.subst(name)
1921 if SCons.Util.is_Sequence(s):
1924 result.append(apply(self.fs.File, (e,) + args, kw))
1926 return apply(self.fs.File, (s,) + args, kw)
1928 def FindFile(self, file, dirs):
1929 file = self.subst(file)
1930 nodes = self.arg2nodes(dirs, self.fs.Dir)
1931 return SCons.Node.FS.find_file(file, tuple(nodes))
1933 def Flatten(self, sequence):
1934 return SCons.Util.flatten(sequence)
1936 def GetBuildPath(self, files):
1937 result = map(str, self.arg2nodes(files, self.fs.Entry))
1938 if SCons.Util.is_List(files):
1943 def Glob(self, pattern, ondisk=True, source=False, strings=False):
1944 return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
1946 def Ignore(self, target, dependency):
1947 """Ignore a dependency."""
1948 tlist = self.arg2nodes(target, self.fs.Entry)
1949 dlist = self.arg2nodes(dependency, self.fs.Entry)
1954 def Literal(self, string):
1955 return SCons.Subst.Literal(string)
1957 def Local(self, *targets):
1959 for targ in targets:
1960 if isinstance(targ, SCons.Node.Node):
1964 for t in self.arg2nodes(targ, self.fs.Entry):
1969 def Precious(self, *targets):
1972 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1977 def Repository(self, *dirs, **kw):
1978 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1979 apply(self.fs.Repository, dirs, kw)
1981 def Requires(self, target, prerequisite):
1982 """Specify that 'prerequisite' must be built before 'target',
1983 (but 'target' does not actually depend on 'prerequisite'
1984 and need not be rebuilt if it changes)."""
1985 tlist = self.arg2nodes(target, self.fs.Entry)
1986 plist = self.arg2nodes(prerequisite, self.fs.Entry)
1988 t.add_prerequisite(plist)
1991 def Scanner(self, *args, **kw):
1994 if SCons.Util.is_String(arg):
1995 arg = self.subst(arg)
1997 nkw = self.subst_kw(kw)
1998 return apply(SCons.Scanner.Base, nargs, nkw)
2000 def SConsignFile(self, name=".sconsign", dbm_module=None):
2001 if not name is None:
2002 name = self.subst(name)
2003 if not os.path.isabs(name):
2004 name = os.path.join(str(self.fs.SConstruct_dir), name)
2006 name = os.path.normpath(name)
2007 sconsign_dir = os.path.dirname(name)
2008 if sconsign_dir and not os.path.exists(sconsign_dir):
2009 self.Execute(SCons.Defaults.Mkdir(sconsign_dir))
2010 SCons.SConsign.File(name, dbm_module)
2012 def SideEffect(self, side_effect, target):
2013 """Tell scons that side_effects are built as side
2014 effects of building targets."""
2015 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
2016 targets = self.arg2nodes(target, self.fs.Entry)
2018 for side_effect in side_effects:
2019 if side_effect.multiple_side_effect_has_builder():
2020 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
2021 side_effect.add_source(targets)
2022 side_effect.side_effect = 1
2023 self.Precious(side_effect)
2024 for target in targets:
2025 target.side_effects.append(side_effect)
2028 def SourceCode(self, entry, builder):
2029 """Arrange for a source code builder for (part of) a tree."""
2030 entries = self.arg2nodes(entry, self.fs.Entry)
2031 for entry in entries:
2032 entry.set_src_builder(builder)
2035 def SourceSignatures(self, type):
2036 global _warn_source_signatures_deprecated
2037 if _warn_source_signatures_deprecated:
2038 msg = "The env.SourceSignatures() method is deprecated;\n" + \
2039 "\tconvert your build to use the env.Decider() method instead."
2040 SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
2041 _warn_source_signatures_deprecated = False
2042 type = self.subst(type)
2043 self.src_sig_type = type
2045 if not SCons.Util.md5:
2046 raise UserError, "MD5 signatures are not available in this version of Python."
2047 self.decide_source = self._changed_content
2048 elif type == 'timestamp':
2049 self.decide_source = self._changed_timestamp_match
2051 raise UserError, "Unknown source signature type '%s'" % type
2053 def Split(self, arg):
2054 """This function converts a string or list into a list of strings
2055 or Nodes. This makes things easier for users by allowing files to
2056 be specified as a white-space separated list to be split.
2057 The input rules are:
2058 - A single string containing names separated by spaces. These will be
2059 split apart at the spaces.
2060 - A single Node instance
2061 - A list containing either strings or Node instances. Any strings
2062 in the list are not split at spaces.
2063 In all cases, the function returns a list of Nodes and strings."""
2064 if SCons.Util.is_List(arg):
2065 return map(self.subst, arg)
2066 elif SCons.Util.is_String(arg):
2067 return string.split(self.subst(arg))
2069 return [self.subst(arg)]
2071 def TargetSignatures(self, type):
2072 global _warn_target_signatures_deprecated
2073 if _warn_target_signatures_deprecated:
2074 msg = "The env.TargetSignatures() method is deprecated;\n" + \
2075 "\tconvert your build to use the env.Decider() method instead."
2076 SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
2077 _warn_target_signatures_deprecated = False
2078 type = self.subst(type)
2079 self.tgt_sig_type = type
2080 if type in ('MD5', 'content'):
2081 if not SCons.Util.md5:
2082 raise UserError, "MD5 signatures are not available in this version of Python."
2083 self.decide_target = self._changed_content
2084 elif type == 'timestamp':
2085 self.decide_target = self._changed_timestamp_match
2086 elif type == 'build':
2087 self.decide_target = self._changed_build
2088 elif type == 'source':
2089 self.decide_target = self._changed_source
2091 raise UserError, "Unknown target signature type '%s'"%type
2093 def Value(self, value, built_value=None):
2096 return SCons.Node.Python.Value(value, built_value)
2098 def VariantDir(self, variant_dir, src_dir, duplicate=1):
2099 variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
2100 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
2101 self.fs.VariantDir(variant_dir, src_dir, duplicate)
2103 def FindSourceFiles(self, node='.'):
2104 """ returns a list of all source files.
2106 node = self.arg2nodes(node, self.fs.Entry)[0]
2109 # Uncomment this and get rid of the global definition when we
2110 # drop support for pre-2.2 Python versions.
2111 #def build_source(ss, result):
2113 # if isinstance(s, SCons.Node.FS.Dir):
2114 # build_source(s.all_children(), result)
2115 # elif s.has_builder():
2116 # build_source(s.sources, result)
2117 # elif isinstance(s.disambiguate(), SCons.Node.FS.File):
2119 build_source(node.all_children(), sources)
2121 # now strip the build_node from the sources by calling the srcnode
2123 def get_final_srcnode(file):
2124 srcnode = file.srcnode()
2125 while srcnode != file.srcnode():
2126 srcnode = file.srcnode()
2129 # get the final srcnode for all nodes, this means stripping any
2130 # attached build node.
2131 map( get_final_srcnode, sources )
2134 return list(set(sources))
2136 def FindInstalledFiles(self):
2137 """ returns the list of all targets of the Install and InstallAs Builder.
2139 from SCons.Tool import install
2140 if install._UNIQUE_INSTALLED_FILES is None:
2141 install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
2142 return install._UNIQUE_INSTALLED_FILES
2144 class OverrideEnvironment(Base):
2145 """A proxy that overrides variables in a wrapped construction
2146 environment by returning values from an overrides dictionary in
2147 preference to values from the underlying subject environment.
2149 This is a lightweight (I hope) proxy that passes through most use of
2150 attributes to the underlying Environment.Base class, but has just
2151 enough additional methods defined to act like a real construction
2152 environment with overridden values. It can wrap either a Base
2153 construction environment, or another OverrideEnvironment, which
2154 can in turn nest arbitrary OverrideEnvironments...
2156 Note that we do *not* call the underlying base class
2157 (SubsitutionEnvironment) initialization, because we get most of those
2158 from proxying the attributes of the subject construction environment.
2159 But because we subclass SubstitutionEnvironment, this class also
2160 has inherited arg2nodes() and subst*() methods; those methods can't
2161 be proxied because they need *this* object's methods to fetch the
2162 values from the overrides dictionary.
2165 if SCons.Memoize.use_memoizer:
2166 __metaclass__ = SCons.Memoize.Memoized_Metaclass
2168 def __init__(self, subject, overrides={}):
2169 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
2170 self.__dict__['__subject'] = subject
2171 self.__dict__['overrides'] = overrides
2173 # Methods that make this class act like a proxy.
2174 def __getattr__(self, name):
2175 return getattr(self.__dict__['__subject'], name)
2176 def __setattr__(self, name, value):
2177 setattr(self.__dict__['__subject'], name, value)
2179 # Methods that make this class act like a dictionary.
2180 def __getitem__(self, key):
2182 return self.__dict__['overrides'][key]
2184 return self.__dict__['__subject'].__getitem__(key)
2185 def __setitem__(self, key, value):
2186 if not is_valid_construction_var(key):
2187 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
2188 self.__dict__['overrides'][key] = value
2189 def __delitem__(self, key):
2191 del self.__dict__['overrides'][key]
2197 result = self.__dict__['__subject'].__delitem__(key)
2203 def get(self, key, default=None):
2204 """Emulates the get() method of dictionaries."""
2206 return self.__dict__['overrides'][key]
2208 return self.__dict__['__subject'].get(key, default)
2209 def has_key(self, key):
2211 self.__dict__['overrides'][key]
2214 return self.__dict__['__subject'].has_key(key)
2215 def __contains__(self, key):
2216 if self.__dict__['overrides'].__contains__(key):
2218 return self.__dict__['__subject'].__contains__(key)
2219 def Dictionary(self):
2220 """Emulates the items() method of dictionaries."""
2221 d = self.__dict__['__subject'].Dictionary().copy()
2222 d.update(self.__dict__['overrides'])
2225 """Emulates the items() method of dictionaries."""
2226 return self.Dictionary().items()
2228 # Overridden private construction environment methods.
2229 def _update(self, dict):
2230 """Update an environment's values directly, bypassing the normal
2231 checks that occur when users try to set items.
2233 self.__dict__['overrides'].update(dict)
2236 return self.__dict__['__subject'].gvars()
2239 lvars = self.__dict__['__subject'].lvars()
2240 lvars.update(self.__dict__['overrides'])
2243 # Overridden public construction environment methods.
2244 def Replace(self, **kw):
2245 kw = copy_non_reserved_keywords(kw)
2246 self.__dict__['overrides'].update(semi_deepcopy(kw))
2248 # The entry point that will be used by the external world
2249 # to refer to a construction environment. This allows the wrapper
2250 # interface to extend a construction environment for its own purposes
2251 # by subclassing SCons.Environment.Base and then assigning the
2252 # class to SCons.Environment.Environment.
2256 # An entry point for returning a proxy subclass instance that overrides
2257 # the subst*() methods so they don't actually perform construction
2258 # variable substitution. This is specifically intended to be the shim
2259 # layer in between global function calls (which don't want construction
2260 # variable substitution) and the DefaultEnvironment() (which would
2261 # substitute variables if left to its own devices)."""
2263 # We have to wrap this in a function that allows us to delay definition of
2264 # the class until it's necessary, so that when it subclasses Environment
2265 # it will pick up whatever Environment subclass the wrapper interface
2266 # might have assigned to SCons.Environment.Environment.
2268 def NoSubstitutionProxy(subject):
2269 class _NoSubstitutionProxy(Environment):
2270 def __init__(self, subject):
2271 self.__dict__['__subject'] = subject
2272 def __getattr__(self, name):
2273 return getattr(self.__dict__['__subject'], name)
2274 def __setattr__(self, name, value):
2275 return setattr(self.__dict__['__subject'], name, value)
2276 def raw_to_mode(self, dict):
2284 def subst(self, string, *args, **kwargs):
2286 def subst_kw(self, kw, *args, **kwargs):
2288 def subst_list(self, string, *args, **kwargs):
2289 nargs = (string, self,) + args
2292 self.raw_to_mode(nkw)
2293 return apply(SCons.Subst.scons_subst_list, nargs, nkw)
2294 def subst_target_source(self, string, *args, **kwargs):
2295 nargs = (string, self,) + args
2298 self.raw_to_mode(nkw)
2299 return apply(SCons.Subst.scons_subst, nargs, nkw)
2300 return _NoSubstitutionProxy(subject)