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
69 _warn_copy_deprecated = True
70 _warn_source_signatures_deprecated = True
71 _warn_target_signatures_deprecated = True
76 semi_deepcopy = SCons.Util.semi_deepcopy
78 # Pull UserError into the global name space for the benefit of
79 # Environment().SourceSignatures(), which has some import statements
80 # which seem to mess up its ability to reference SCons directly.
81 UserError = SCons.Errors.UserError
83 def alias_builder(env, target, source):
86 AliasBuilder = SCons.Builder.Builder(action = alias_builder,
87 target_factory = SCons.Node.Alias.default_ans.Alias,
88 source_factory = SCons.Node.FS.Entry,
93 def apply_tools(env, tools, toolpath):
94 # Store the toolpath in the Environment.
95 if toolpath is not None:
96 env['toolpath'] = toolpath
100 # Filter out null tools from the list.
101 for tool in filter(None, tools):
102 if SCons.Util.is_List(tool) or type(tool)==type(()):
104 toolargs = tool[1] # should be a dict of kw args
105 tool = apply(env.Tool, [toolname], toolargs)
109 # These names are (or will be) controlled by SCons; users should never
110 # set or override them. This warning can optionally be turned off,
111 # but scons will still ignore the illegal variable names even if it's off.
112 reserved_construction_var_names = [
123 future_reserved_construction_var_names = [
129 def copy_non_reserved_keywords(dict):
130 result = semi_deepcopy(dict)
131 for k in result.keys():
132 if k in reserved_construction_var_names:
133 msg = "Ignoring attempt to set reserved variable `$%s'"
134 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k)
138 def _set_reserved(env, key, value):
139 msg = "Ignoring attempt to set reserved variable `$%s'"
140 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key)
142 def _set_future_reserved(env, key, value):
143 env._dict[key] = value
144 msg = "`$%s' will be reserved in a future release and setting it will become ignored"
145 SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key)
147 def _set_BUILDERS(env, key, value):
153 bd = BuilderDict(kwbd, env)
155 for k, v in value.items():
156 if not SCons.Builder.is_a_Builder(v):
157 raise SCons.Errors.UserError('%s is not a Builder.' % repr(v))
160 def _del_SCANNERS(env, key):
162 env.scanner_map_delete()
164 def _set_SCANNERS(env, key, value):
165 env._dict[key] = value
166 env.scanner_map_delete()
168 def _delete_duplicates(l, keep_last):
169 """Delete duplicates from a sequence, keeping the first or last."""
172 if keep_last: # reverse in & out, then keep first
176 if not seen.has_key(i):
180 # probably unhashable. Just keep it.
188 # The following is partly based on code in a comment added by Peter
189 # Shannon at the following page (there called the "transplant" class):
191 # ASPN : Python Cookbook : Dynamically added methods to a class
192 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
194 # We had independently been using the idiom as BuilderWrapper, but
195 # factoring out the common parts into this base class, and making
196 # BuilderWrapper a subclass that overrides __call__() to enforce specific
197 # Builder calling conventions, simplified some of our higher-layer code.
201 A generic Wrapper class that associates a method (which can
202 actually be any callable) with an object. As part of creating this
203 MethodWrapper object an attribute with the specified (by default,
204 the name of the supplied method) is added to the underlying object.
205 When that new "method" is called, our __call__() method adds the
206 object as the first argument, simulating the Python behavior of
207 supplying "self" on method calls.
209 We hang on to the name by which the method was added to the underlying
210 base class so that we can provide a method to "clone" ourselves onto
211 a new underlying object being copied (without which we wouldn't need
214 def __init__(self, object, method, name=None):
216 name = method.__name__
220 setattr(self.object, name, self)
222 def __call__(self, *args, **kwargs):
223 nargs = (self.object,) + args
224 return apply(self.method, nargs, kwargs)
226 def clone(self, new_object):
228 Returns an object that re-binds the underlying "method" to
229 the specified new object.
231 return self.__class__(new_object, self.method, self.name)
233 class BuilderWrapper(MethodWrapper):
235 A MethodWrapper subclass that that associates an environment with
238 This mainly exists to wrap the __call__() function so that all calls
239 to Builders can have their argument lists massaged in the same way
240 (treat a lone argument as the source, treat two arguments as target
241 then source, make sure both target and source are lists) without
242 having to have cut-and-paste code to do it.
244 As a bit of obsessive backwards compatibility, we also intercept
245 attempts to get or set the "env" or "builder" attributes, which were
246 the names we used before we put the common functionality into the
247 MethodWrapper base class. We'll keep this around for a while in case
248 people shipped Tool modules that reached into the wrapper (like the
249 Tool/qt.py module does, or did). There shouldn't be a lot attribute
250 fetching or setting on these, so a little extra work shouldn't hurt.
252 def __call__(self, target=None, source=_null, *args, **kw):
256 if target is not None and not SCons.Util.is_List(target):
258 if source is not None and not SCons.Util.is_List(source):
260 return apply(MethodWrapper.__call__, (self, target, source) + args, kw)
263 return '<BuilderWrapper %s>' % repr(self.name)
266 return self.__repr__()
268 def __getattr__(self, name):
271 elif name == 'builder':
274 raise AttributeError, name
276 def __setattr__(self, name, value):
279 elif name == 'builder':
282 self.__dict__[name] = value
284 # This allows a Builder to be executed directly
285 # through the Environment to which it's attached.
286 # In practice, we shouldn't need this, because
287 # builders actually get executed through a Node.
288 # But we do have a unit test for this, and can't
289 # yet rule out that it would be useful in the
290 # future, so leave it for now.
291 #def execute(self, **kw):
292 # kw['env'] = self.env
293 # apply(self.builder.execute, (), kw)
295 class BuilderDict(UserDict):
296 """This is a dictionary-like class used by an Environment to hold
297 the Builders. We need to do this because every time someone changes
298 the Builders in the Environment's BUILDERS dictionary, we must
299 update the Environment's attributes."""
300 def __init__(self, dict, env):
301 # Set self.env before calling the superclass initialization,
302 # because it will end up calling our other methods, which will
303 # need to point the values in this dictionary to self.env.
305 UserDict.__init__(self, dict)
307 def __semi_deepcopy__(self):
308 return self.__class__(self.data, self.env)
310 def __setitem__(self, item, val):
312 method = getattr(self.env, item).method
313 except AttributeError:
316 self.env.RemoveMethod(method)
317 UserDict.__setitem__(self, item, val)
318 BuilderWrapper(self.env, val, item)
320 def __delitem__(self, item):
321 UserDict.__delitem__(self, item)
322 delattr(self.env, item)
324 def update(self, dict):
325 for i, v in dict.items():
326 self.__setitem__(i, v)
330 _is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
332 def is_valid_construction_var(varstr):
333 """Return if the specified string is a legitimate construction
336 return _is_valid_var.match(varstr)
340 class SubstitutionEnvironment:
341 """Base class for different flavors of construction environments.
343 This class contains a minimal set of methods that handle contruction
344 variable expansion and conversion of strings to Nodes, which may or
345 may not be actually useful as a stand-alone class. Which methods
346 ended up in this class is pretty arbitrary right now. They're
347 basically the ones which we've empirically determined are common to
348 the different construction environment subclasses, and most of the
349 others that use or touch the underlying dictionary of construction
352 Eventually, this class should contain all the methods that we
353 determine are necessary for a "minimal" interface to the build engine.
354 A full "native Python" SCons environment has gotten pretty heavyweight
355 with all of the methods and Tools and construction variables we've
356 jammed in there, so it would be nice to have a lighter weight
357 alternative for interfaces that don't need all of the bells and
358 whistles. (At some point, we'll also probably rename this class
359 "Base," since that more reflects what we want this class to become,
360 but because we've released comments that tell people to subclass
361 Environment.Base to create their own flavors of construction
362 environment, we'll save that for a future refactoring when this
363 class actually becomes useful.)
366 if SCons.Memoize.use_memoizer:
367 __metaclass__ = SCons.Memoize.Memoized_Metaclass
369 def __init__(self, **kw):
370 """Initialization of an underlying SubstitutionEnvironment class.
372 if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
373 self.fs = SCons.Node.FS.get_default_fs()
374 self.ans = SCons.Node.Alias.default_ans
375 self.lookup_list = SCons.Node.arg2nodes_lookups
376 self._dict = kw.copy()
378 self.added_methods = []
381 def _init_special(self):
382 """Initial the dispatch tables for special handling of
383 special construction variables."""
384 self._special_del = {}
385 self._special_del['SCANNERS'] = _del_SCANNERS
387 self._special_set = {}
388 for key in reserved_construction_var_names:
389 self._special_set[key] = _set_reserved
390 for key in future_reserved_construction_var_names:
391 self._special_set[key] = _set_future_reserved
392 self._special_set['BUILDERS'] = _set_BUILDERS
393 self._special_set['SCANNERS'] = _set_SCANNERS
395 # Freeze the keys of self._special_set in a list for use by
396 # methods that need to check. (Empirically, list scanning has
397 # gotten better than dict.has_key() in Python 2.5.)
398 self._special_set_keys = self._special_set.keys()
400 def __cmp__(self, other):
401 return cmp(self._dict, other._dict)
403 def __delitem__(self, key):
404 special = self._special_del.get(key)
410 def __getitem__(self, key):
411 return self._dict[key]
413 def __setitem__(self, key, value):
414 # This is heavily used. This implementation is the best we have
415 # according to the timings in bench/env.__setitem__.py.
417 # The "key in self._special_set_keys" test here seems to perform
418 # pretty well for the number of keys we have. A hard-coded
419 # list works a little better in Python 2.5, but that has the
420 # disadvantage of maybe getting out of sync if we ever add more
421 # variable names. Using self._special_set.has_key() works a
422 # little better in Python 2.4, but is worse then this test.
423 # So right now it seems like a good trade-off, but feel free to
424 # revisit this with bench/env.__setitem__.py as needed (and
425 # as newer versions of Python come out).
426 if key in self._special_set_keys:
427 self._special_set[key](self, key, value)
429 # If we already have the entry, then it's obviously a valid
430 # key and we don't need to check. If we do check, using a
431 # global, pre-compiled regular expression directly is more
432 # efficient than calling another function or a method.
433 if not self._dict.has_key(key) \
434 and not _is_valid_var.match(key):
435 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
436 self._dict[key] = value
438 def get(self, key, default=None):
439 """Emulates the get() method of dictionaries."""
440 return self._dict.get(key, default)
442 def has_key(self, key):
443 return self._dict.has_key(key)
445 def __contains__(self, key):
446 return self._dict.__contains__(key)
449 return self._dict.items()
451 def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
452 if node_factory is _null:
453 node_factory = self.fs.File
454 if lookup_list is _null:
455 lookup_list = self.lookup_list
460 args = SCons.Util.flatten(args)
464 if SCons.Util.is_String(v):
466 for l in lookup_list:
471 if SCons.Util.is_String(n):
472 # n = self.subst(n, raw=1, **kw)
474 n = apply(self.subst, (n,), kw)
477 if SCons.Util.is_List(n):
482 # v = node_factory(self.subst(v, raw=1, **kw))
484 v = node_factory(apply(self.subst, (v,), kw))
485 if SCons.Util.is_List(v):
500 def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None):
501 """Recursively interpolates construction variables from the
502 Environment into the specified string, returning the expanded
503 result. Construction variables are specified by a $ prefix
504 in the string and begin with an initial underscore or
505 alphabetic character followed by any number of underscores
506 or alphanumeric characters. The construction variable names
507 may be surrounded by curly braces to separate the name from
512 lvars['__env__'] = self
514 lvars.update(executor.get_lvars())
515 return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
517 def subst_kw(self, kw, raw=0, target=None, source=None):
519 for k, v in kw.items():
520 k = self.subst(k, raw, target, source)
521 if SCons.Util.is_String(v):
522 v = self.subst(v, raw, target, source)
526 def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None):
527 """Calls through to SCons.Subst.scons_subst_list(). See
528 the documentation for that function."""
531 lvars['__env__'] = self
533 lvars.update(executor.get_lvars())
534 return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
536 def subst_path(self, path, target=None, source=None):
537 """Substitute a path list, turning EntryProxies into Nodes
538 and leaving Nodes (and other objects) as-is."""
540 if not SCons.Util.is_List(path):
544 """This is the "string conversion" routine that we have our
545 substitutions use to return Nodes, not strings. This relies
546 on the fact that an EntryProxy object has a get() method that
547 returns the underlying Node that it wraps, which is a bit of
548 architectural dependence that we might need to break or modify
549 in the future in response to additional requirements."""
552 except AttributeError:
553 obj = SCons.Util.to_String_for_subst(obj)
560 if SCons.Util.is_String(p):
561 p = self.subst(p, target=target, source=source, conv=s)
562 if SCons.Util.is_List(p):
566 # We have an object plus a string, or multiple
567 # objects that we need to smush together. No choice
568 # but to make them into a string.
569 p = string.join(map(SCons.Util.to_String_for_subst, p), '')
575 subst_target_source = subst
577 def backtick(self, command):
580 kw = { 'stdin' : 'devnull',
581 'stdout' : subprocess.PIPE,
582 'stderr' : subprocess.PIPE,
583 'universal_newlines' : True,
585 # if the command is a list, assume it's been quoted
586 # othewise force a shell
587 if not SCons.Util.is_List(command): kw['shell'] = True
588 # run constructed command
589 #TODO(1.5) p = SCons.Action._subproc(self, command, **kw)
590 p = apply(SCons.Action._subproc, (self, command), kw)
591 out,err = p.communicate()
594 sys.stderr.write(err)
596 raise OSError("'%s' exited %d" % (command, status))
599 def AddMethod(self, function, name=None):
601 Adds the specified function as a method of this construction
602 environment with the specified name. If the name is omitted,
603 the default name is the name of the function itself.
605 method = MethodWrapper(self, function, name)
606 self.added_methods.append(method)
608 def RemoveMethod(self, function):
610 Removes the specified function's MethodWrapper from the
611 added_methods list, so we don't re-bind it when making a clone.
613 is_not_func = lambda dm, f=function: not dm.method is f
614 self.added_methods = filter(is_not_func, self.added_methods)
616 def Override(self, overrides):
618 Produce a modified environment whose variables are overriden by
619 the overrides dictionaries. "overrides" is a dictionary that
620 will override the variables of this environment.
622 This function is much more efficient than Clone() or creating
623 a new Environment because it doesn't copy the construction
624 environment dictionary, it just wraps the underlying construction
625 environment, and doesn't even create a wrapper object if there
628 if not overrides: return self
629 o = copy_non_reserved_keywords(overrides)
630 if not o: return self
633 for key, value in o.items():
634 if key == 'parse_flags':
637 overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
638 env = OverrideEnvironment(self, overrides)
639 if merges: env.MergeFlags(merges)
642 def ParseFlags(self, *flags):
644 Parse the set of flags and return a dict with the flags placed
645 in the appropriate entry. The flags are treated as a typical
646 set of command-line flags for a GNU-like toolchain and used to
647 populate the entries in the dict immediately below. If one of
648 the flag strings begins with a bang (exclamation mark), it is
649 assumed to be a command and the rest of the string is executed;
650 the result of that evaluation is then added to the dict.
653 'ASFLAGS' : SCons.Util.CLVar(''),
654 'CFLAGS' : SCons.Util.CLVar(''),
655 'CCFLAGS' : SCons.Util.CLVar(''),
657 'CPPFLAGS' : SCons.Util.CLVar(''),
659 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
660 'FRAMEWORKS' : SCons.Util.CLVar(''),
663 'LINKFLAGS' : SCons.Util.CLVar(''),
667 # The use of the "me" parameter to provide our own name for
668 # recursion is an egregious hack to support Python 2.1 and before.
669 def do_parse(arg, me, self = self, dict = dict):
670 # if arg is a sequence, recurse with each element
674 if not SCons.Util.is_String(arg):
675 for t in arg: me(t, me)
678 # if arg is a command, execute it
680 arg = self.backtick(arg[1:])
682 # utility function to deal with -D option
683 def append_define(name, dict = dict):
684 t = string.split(name, '=')
686 dict['CPPDEFINES'].append(name)
688 dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
690 # Loop through the flags and add them to the appropriate option.
691 # This tries to strike a balance between checking for all possible
692 # flags and keeping the logic to a finite size, so it doesn't
693 # check for some that don't occur often. It particular, if the
694 # flag is not known to occur in a config script and there's a way
695 # of passing the flag to the right place (by wrapping it in a -W
696 # flag, for example) we don't check for it. Note that most
697 # preprocessor options are not handled, since unhandled options
698 # are placed in CCFLAGS, so unless the preprocessor is invoked
699 # separately, these flags will still get to the preprocessor.
700 # Other options not currently handled:
701 # -iqoutedir (preprocessor search path)
702 # -u symbol (linker undefined symbol)
703 # -s (linker strip files)
704 # -static* (linker static binding)
705 # -shared* (linker dynamic binding)
706 # -symbolic (linker global binding)
707 # -R dir (deprecated linker rpath)
708 # IBM compilers may also accept -qframeworkdir=foo
710 params = shlex.split(arg)
711 append_next_arg_to = None # for multi-word args
713 if append_next_arg_to:
714 if append_next_arg_to == 'CPPDEFINES':
716 elif append_next_arg_to == '-include':
717 t = ('-include', self.fs.File(arg))
718 dict['CCFLAGS'].append(t)
719 elif append_next_arg_to == '-isysroot':
720 t = ('-isysroot', arg)
721 dict['CCFLAGS'].append(t)
722 dict['LINKFLAGS'].append(t)
723 elif append_next_arg_to == '-arch':
725 dict['CCFLAGS'].append(t)
726 dict['LINKFLAGS'].append(t)
728 dict[append_next_arg_to].append(arg)
729 append_next_arg_to = None
730 elif not arg[0] in ['-', '+']:
731 dict['LIBS'].append(self.fs.File(arg))
732 elif arg[:2] == '-L':
734 dict['LIBPATH'].append(arg[2:])
736 append_next_arg_to = 'LIBPATH'
737 elif arg[:2] == '-l':
739 dict['LIBS'].append(arg[2:])
741 append_next_arg_to = 'LIBS'
742 elif arg[:2] == '-I':
744 dict['CPPPATH'].append(arg[2:])
746 append_next_arg_to = 'CPPPATH'
747 elif arg[:4] == '-Wa,':
748 dict['ASFLAGS'].append(arg[4:])
749 dict['CCFLAGS'].append(arg)
750 elif arg[:4] == '-Wl,':
751 if arg[:11] == '-Wl,-rpath=':
752 dict['RPATH'].append(arg[11:])
753 elif arg[:7] == '-Wl,-R,':
754 dict['RPATH'].append(arg[7:])
755 elif arg[:6] == '-Wl,-R':
756 dict['RPATH'].append(arg[6:])
758 dict['LINKFLAGS'].append(arg)
759 elif arg[:4] == '-Wp,':
760 dict['CPPFLAGS'].append(arg)
761 elif arg[:2] == '-D':
763 append_define(arg[2:])
765 append_next_arg_to = 'CPPDEFINES'
766 elif arg == '-framework':
767 append_next_arg_to = 'FRAMEWORKS'
768 elif arg[:14] == '-frameworkdir=':
769 dict['FRAMEWORKPATH'].append(arg[14:])
770 elif arg[:2] == '-F':
772 dict['FRAMEWORKPATH'].append(arg[2:])
774 append_next_arg_to = 'FRAMEWORKPATH'
775 elif arg == '-mno-cygwin':
776 dict['CCFLAGS'].append(arg)
777 dict['LINKFLAGS'].append(arg)
778 elif arg == '-mwindows':
779 dict['LINKFLAGS'].append(arg)
780 elif arg == '-pthread':
781 dict['CCFLAGS'].append(arg)
782 dict['LINKFLAGS'].append(arg)
783 elif arg[:5] == '-std=':
784 dict['CFLAGS'].append(arg) # C only
786 dict['CCFLAGS'].append(arg)
787 dict['LINKFLAGS'].append(arg)
788 elif arg in ['-include', '-isysroot', '-arch']:
789 append_next_arg_to = arg
791 dict['CCFLAGS'].append(arg)
794 do_parse(arg, do_parse)
797 def MergeFlags(self, args, unique=1, dict=None):
799 Merge the dict in args into the construction variables of this
800 env, or the passed-in dict. If args is not a dict, it is
801 converted into a dict using ParseFlags. If unique is not set,
802 the flags are appended rather than merged.
807 if not SCons.Util.is_Dict(args):
808 args = self.ParseFlags(args)
810 apply(self.Append, (), args)
812 for key, value in args.items():
823 # Add orig and value. The logic here was lifted from
824 # part of env.Append() (see there for a lot of comments
825 # about the order in which things are tried) and is
826 # used mainly to handle coercion of strings to CLVar to
827 # "do the right thing" given (e.g.) an original CCFLAGS
828 # string variable like '-pipe -Wall'.
831 except (KeyError, TypeError):
833 add_to_orig = orig.append
834 except AttributeError:
835 value.insert(0, orig)
840 if key[-4:] == 'PATH':
841 ### keep left-most occurence
846 ### keep right-most occurence
854 # def MergeShellPaths(self, args, prepend=1):
856 # Merge the dict in args into the shell environment in env['ENV'].
857 # Shell path elements are appended or prepended according to prepend.
859 # Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
861 # Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'})
862 # prepends /usr/local/lib to env['ENV']['LIBPATH'].
865 # for pathname, pathval in args.items():
869 # apply(self.PrependENVPath, (pathname, pathval))
871 # apply(self.AppendENVPath, (pathname, pathval))
874 # Used by the FindSourceFiles() method, below.
875 # Stuck here for support of pre-2.2 Python versions.
876 def build_source(ss, result):
878 if isinstance(s, SCons.Node.FS.Dir):
879 build_source(s.all_children(), result)
880 elif s.has_builder():
881 build_source(s.sources, result)
882 elif isinstance(s.disambiguate(), SCons.Node.FS.File):
885 def default_decide_source(dependency, target, prev_ni):
886 f = SCons.Defaults.DefaultEnvironment().decide_source
887 return f(dependency, target, prev_ni)
889 def default_decide_target(dependency, target, prev_ni):
890 f = SCons.Defaults.DefaultEnvironment().decide_target
891 return f(dependency, target, prev_ni)
893 def default_copy_from_cache(src, dst):
894 f = SCons.Defaults.DefaultEnvironment().copy_from_cache
897 class Base(SubstitutionEnvironment):
898 """Base class for "real" construction Environments. These are the
899 primary objects used to communicate dependency and construction
900 information to the build engine.
902 Keyword arguments supplied when the construction Environment
903 is created are construction variables used to initialize the
907 memoizer_counters = []
909 #######################################################################
910 # This is THE class for interacting with the SCons build engine,
911 # and it contains a lot of stuff, so we're going to try to keep this
912 # a little organized by grouping the methods.
913 #######################################################################
915 #######################################################################
916 # Methods that make an Environment act like a dictionary. These have
917 # the expected standard names for Python mapping objects. Note that
918 # we don't actually make an Environment a subclass of UserDict for
919 # performance reasons. Note also that we only supply methods for
920 # dictionary functionality that we actually need and use.
921 #######################################################################
931 Initialization of a basic SCons construction environment,
932 including setting up special construction variables like BUILDER,
933 PLATFORM, etc., and searching for and applying available Tools.
935 Note that we do *not* call the underlying base class
936 (SubsitutionEnvironment) initialization, because we need to
937 initialize things in a very specific order that doesn't work
938 with the much simpler base class initialization.
940 if __debug__: logInstanceCreation(self, 'Environment.Base')
942 self.fs = SCons.Node.FS.get_default_fs()
943 self.ans = SCons.Node.Alias.default_ans
944 self.lookup_list = SCons.Node.arg2nodes_lookups
945 self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment)
947 self.added_methods = []
949 # We don't use AddMethod, or define these as methods in this
950 # class, because we *don't* want these functions to be bound
951 # methods. They need to operate independently so that the
952 # settings will work properly regardless of whether a given
953 # target ends up being built with a Base environment or an
954 # OverrideEnvironment or what have you.
955 self.decide_target = default_decide_target
956 self.decide_source = default_decide_source
958 self.copy_from_cache = default_copy_from_cache
960 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
963 platform = self._dict.get('PLATFORM', None)
965 platform = SCons.Platform.Platform()
966 if SCons.Util.is_String(platform):
967 platform = SCons.Platform.Platform(platform)
968 self._dict['PLATFORM'] = str(platform)
971 self._dict['HOST_OS'] = self._dict.get('HOST_OS',None)
972 self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None)
974 # Now set defaults for TARGET_{OS|ARCH}
975 self._dict['TARGET_OS'] = self._dict.get('HOST_OS',None)
976 self._dict['TARGET_ARCH'] = self._dict.get('HOST_ARCH',None)
979 # Apply the passed-in and customizable variables to the
980 # environment before calling the tools, because they may use
981 # some of them during initialization.
982 if kw.has_key('options'):
983 # Backwards compatibility: they may stll be using the
984 # old "options" keyword.
985 variables = kw['options']
987 apply(self.Replace, (), kw)
990 keys = keys + variables.keys()
991 variables.Update(self)
996 save[k] = self._dict[k]
998 # No value may have been set if they tried to pass in a
999 # reserved variable name like TARGETS.
1002 SCons.Tool.Initializers(self)
1005 tools = self._dict.get('TOOLS', None)
1008 apply_tools(self, tools, toolpath)
1010 # Now restore the passed-in and customized variables
1011 # to the environment, since the values the user set explicitly
1012 # should override any values set by the tools.
1013 for key, val in save.items():
1014 self._dict[key] = val
1016 # Finally, apply any flags to be merged in
1017 if parse_flags: self.MergeFlags(parse_flags)
1019 #######################################################################
1020 # Utility methods that are primarily for internal use by SCons.
1021 # These begin with lower-case letters.
1022 #######################################################################
1024 def get_builder(self, name):
1025 """Fetch the builder with the specified name from the environment.
1028 return self._dict['BUILDERS'][name]
1032 def get_CacheDir(self):
1034 path = self._CacheDir_path
1035 except AttributeError:
1036 path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
1038 if path == self._last_CacheDir_path:
1039 return self._last_CacheDir
1040 except AttributeError:
1042 cd = SCons.CacheDir.CacheDir(path)
1043 self._last_CacheDir_path = path
1044 self._last_CacheDir = cd
1047 def get_factory(self, factory, default='File'):
1048 """Return a factory function for creating Nodes for this
1049 construction environment.
1053 is_node = issubclass(factory, SCons.Node.FS.Base)
1055 # The specified factory isn't a Node itself--it's
1056 # most likely None, or possibly a callable.
1060 # The specified factory is a Node (sub)class. Try to
1061 # return the FS method that corresponds to the Node's
1062 # name--that is, we return self.fs.Dir if they want a Dir,
1063 # self.fs.File for a File, etc.
1064 try: name = factory.__name__
1065 except AttributeError: pass
1066 else: factory = None
1068 # They passed us None, or we picked up a name from a specified
1069 # class, so return the FS method. (Note that we *don't*
1070 # use our own self.{Dir,File} methods because that would
1071 # cause env.subst() to be called twice on the file name,
1072 # interfering with files that have $$ in them.)
1073 factory = getattr(self.fs, name)
1076 memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
1080 return self._memo['_gsm']
1087 scanners = self._dict['SCANNERS']
1091 # Reverse the scanner list so that, if multiple scanners
1092 # claim they can scan the same suffix, earlier scanners
1093 # in the list will overwrite later scanners, so that
1094 # the result looks like a "first match" to the user.
1095 if not SCons.Util.is_List(scanners):
1096 scanners = [scanners]
1098 scanners = scanners[:] # copy so reverse() doesn't mod original
1100 for scanner in scanners:
1101 for k in scanner.get_skeys(self):
1102 if k and self['PLATFORM'] == 'win32':
1106 self._memo['_gsm'] = result
1110 def get_scanner(self, skey):
1111 """Find the appropriate scanner given a key (usually a file suffix).
1113 if skey and self['PLATFORM'] == 'win32':
1114 skey = string.lower(skey)
1115 return self._gsm().get(skey)
1117 def scanner_map_delete(self, kw=None):
1118 """Delete the cached scanner map (if we need to).
1121 del self._memo['_gsm']
1125 def _update(self, dict):
1126 """Update an environment's values directly, bypassing the normal
1127 checks that occur when users try to set items.
1129 self._dict.update(dict)
1131 def get_src_sig_type(self):
1133 return self.src_sig_type
1134 except AttributeError:
1135 t = SCons.Defaults.DefaultEnvironment().src_sig_type
1136 self.src_sig_type = t
1139 def get_tgt_sig_type(self):
1141 return self.tgt_sig_type
1142 except AttributeError:
1143 t = SCons.Defaults.DefaultEnvironment().tgt_sig_type
1144 self.tgt_sig_type = t
1147 #######################################################################
1148 # Public methods for manipulating an Environment. These begin with
1149 # upper-case letters. The essential characteristic of methods in
1150 # this section is that they do *not* have corresponding same-named
1151 # global functions. For example, a stand-alone Append() function
1152 # makes no sense, because Append() is all about appending values to
1153 # an Environment's construction variables.
1154 #######################################################################
1156 def Append(self, **kw):
1157 """Append values to existing construction variables
1160 kw = copy_non_reserved_keywords(kw)
1161 for key, val in kw.items():
1162 # It would be easier on the eyes to write this using
1163 # "continue" statements whenever we finish processing an item,
1164 # but Python 1.5.2 apparently doesn't let you use "continue"
1165 # within try:-except: blocks, so we have to nest our code.
1167 orig = self._dict[key]
1169 # No existing variable in the environment, so just set
1170 # it to the new value.
1171 self._dict[key] = val
1174 # Check if the original looks like a dictionary.
1175 # If it is, we can't just try adding the value because
1176 # dictionaries don't have __add__() methods, and
1177 # things like UserList will incorrectly coerce the
1178 # original dict to a list (which we don't want).
1179 update_dict = orig.update
1180 except AttributeError:
1182 # Most straightforward: just try to add them
1183 # together. This will work in most cases, when the
1184 # original and new values are of compatible types.
1185 self._dict[key] = orig + val
1186 except (KeyError, TypeError):
1188 # Check if the original is a list.
1189 add_to_orig = orig.append
1190 except AttributeError:
1191 # The original isn't a list, but the new
1192 # value is (by process of elimination),
1193 # so insert the original in the new value
1194 # (if there's one to insert) and replace
1195 # the variable with it.
1198 self._dict[key] = val
1200 # The original is a list, so append the new
1201 # value to it (if there's a value to append).
1205 # The original looks like a dictionary, so update it
1206 # based on what we think the value looks like.
1207 if SCons.Util.is_List(val):
1213 except (AttributeError, TypeError, ValueError):
1214 if SCons.Util.is_Dict(val):
1215 for k, v in val.items():
1219 self.scanner_map_delete(kw)
1221 # allow Dirs and strings beginning with # for top-relative
1222 # Note this uses the current env's fs (in self).
1223 def _canonicalize(self, path):
1224 if not SCons.Util.is_String(path): # typically a Dir
1226 if path and path[0] == '#':
1227 path = str(self.fs.Dir(path))
1230 def AppendENVPath(self, name, newpath, envname = 'ENV',
1231 sep = os.pathsep, delete_existing=1):
1232 """Append path elements to the path 'name' in the 'ENV'
1233 dictionary for this environment. Will only add any particular
1234 path once, and will normpath and normcase all paths to help
1235 assure this. This can also handle the case where the env
1236 variable is a list instead of a string.
1238 If delete_existing is 0, a newpath which is already in the path
1239 will not be moved to the end (it will be left where it is).
1243 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1244 orig = self._dict[envname][name]
1246 nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing,
1247 canonicalize=self._canonicalize)
1249 if not self._dict.has_key(envname):
1250 self._dict[envname] = {}
1252 self._dict[envname][name] = nv
1254 def AppendUnique(self, delete_existing=0, **kw):
1255 """Append values to existing construction variables
1256 in an Environment, if they're not already there.
1257 If delete_existing is 1, removes existing values first, so
1260 kw = copy_non_reserved_keywords(kw)
1261 for key, val in kw.items():
1262 if SCons.Util.is_List(val):
1263 val = _delete_duplicates(val, delete_existing)
1264 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1265 self._dict[key] = val
1266 elif SCons.Util.is_Dict(self._dict[key]) and \
1267 SCons.Util.is_Dict(val):
1268 self._dict[key].update(val)
1269 elif SCons.Util.is_List(val):
1270 dk = self._dict[key]
1271 if not SCons.Util.is_List(dk):
1274 dk = filter(lambda x, val=val: x not in val, dk)
1276 val = filter(lambda x, dk=dk: x not in dk, val)
1277 self._dict[key] = dk + val
1279 dk = self._dict[key]
1280 if SCons.Util.is_List(dk):
1281 # By elimination, val is not a list. Since dk is a
1282 # list, wrap val in a list first.
1284 dk = filter(lambda x, val=val: x not in val, dk)
1285 self._dict[key] = dk + [val]
1288 self._dict[key] = dk + [val]
1291 dk = filter(lambda x, val=val: x not in val, dk)
1292 self._dict[key] = dk + val
1293 self.scanner_map_delete(kw)
1295 def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
1296 """Return a copy of a construction Environment. The
1297 copy is like a Python "deep copy"--that is, independent
1298 copies are made recursively of each objects--except that
1299 a reference is copied when an object is not deep-copyable
1300 (like a function). There are no references to any mutable
1301 objects in the original Environment.
1303 clone = copy.copy(self)
1304 clone._dict = semi_deepcopy(self._dict)
1307 cbd = clone._dict['BUILDERS']
1311 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
1313 # Check the methods added via AddMethod() and re-bind them to
1314 # the cloned environment. Only do this if the attribute hasn't
1315 # been overwritten by the user explicitly and still points to
1317 clone.added_methods = []
1318 for mw in self.added_methods:
1319 if mw == getattr(self, mw.name):
1320 clone.added_methods.append(mw.clone(clone))
1324 # Apply passed-in variables before the tools
1325 # so the tools can use the new variables
1326 kw = copy_non_reserved_keywords(kw)
1328 for key, value in kw.items():
1329 new[key] = SCons.Subst.scons_subst_once(value, self, key)
1330 apply(clone.Replace, (), new)
1332 apply_tools(clone, tools, toolpath)
1334 # apply them again in case the tools overwrote them
1335 apply(clone.Replace, (), new)
1337 # Finally, apply any flags to be merged in
1338 if parse_flags: clone.MergeFlags(parse_flags)
1340 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
1343 def Copy(self, *args, **kw):
1344 global _warn_copy_deprecated
1345 if _warn_copy_deprecated:
1346 msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
1347 SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
1348 _warn_copy_deprecated = False
1349 return apply(self.Clone, args, kw)
1351 def _changed_build(self, dependency, target, prev_ni):
1352 if dependency.changed_state(target, prev_ni):
1354 return self.decide_source(dependency, target, prev_ni)
1356 def _changed_content(self, dependency, target, prev_ni):
1357 return dependency.changed_content(target, prev_ni)
1359 def _changed_source(self, dependency, target, prev_ni):
1360 target_env = dependency.get_build_env()
1361 type = target_env.get_tgt_sig_type()
1362 if type == 'source':
1363 return target_env.decide_source(dependency, target, prev_ni)
1365 return target_env.decide_target(dependency, target, prev_ni)
1367 def _changed_timestamp_then_content(self, dependency, target, prev_ni):
1368 return dependency.changed_timestamp_then_content(target, prev_ni)
1370 def _changed_timestamp_newer(self, dependency, target, prev_ni):
1371 return dependency.changed_timestamp_newer(target, prev_ni)
1373 def _changed_timestamp_match(self, dependency, target, prev_ni):
1374 return dependency.changed_timestamp_match(target, prev_ni)
1376 def _copy_from_cache(self, src, dst):
1377 return self.fs.copy(src, dst)
1379 def _copy2_from_cache(self, src, dst):
1380 return self.fs.copy2(src, dst)
1382 def Decider(self, function):
1383 copy_function = self._copy2_from_cache
1384 if function in ('MD5', 'content'):
1385 if not SCons.Util.md5:
1386 raise UserError, "MD5 signatures are not available in this version of Python."
1387 function = self._changed_content
1388 elif function == 'MD5-timestamp':
1389 function = self._changed_timestamp_then_content
1390 elif function in ('timestamp-newer', 'make'):
1391 function = self._changed_timestamp_newer
1392 copy_function = self._copy_from_cache
1393 elif function == 'timestamp-match':
1394 function = self._changed_timestamp_match
1395 elif not callable(function):
1396 raise UserError, "Unknown Decider value %s" % repr(function)
1398 # We don't use AddMethod because we don't want to turn the
1399 # function, which only expects three arguments, into a bound
1400 # method, which would add self as an initial, fourth argument.
1401 self.decide_target = function
1402 self.decide_source = function
1404 self.copy_from_cache = copy_function
1406 def Detect(self, progs):
1407 """Return the first available program in progs.
1409 if not SCons.Util.is_List(progs):
1412 path = self.WhereIs(prog)
1413 if path: return prog
1416 def Dictionary(self, *args):
1419 dlist = map(lambda x, s=self: s._dict[x], args)
1424 def Dump(self, key = None):
1426 Using the standard Python pretty printer, dump the contents of the
1427 scons build environment to stdout.
1429 If the key passed in is anything other than None, then that will
1430 be used as an index into the build environment dictionary and
1431 whatever is found there will be fed into the pretty printer. Note
1432 that this key is case sensitive.
1435 pp = pprint.PrettyPrinter(indent=2)
1437 dict = self.Dictionary(key)
1439 dict = self.Dictionary()
1440 return pp.pformat(dict)
1442 def FindIxes(self, paths, prefix, suffix):
1444 Search a list of paths for something that matches the prefix and suffix.
1446 paths - the list of paths or nodes.
1447 prefix - construction variable for the prefix.
1448 suffix - construction variable for the suffix.
1451 suffix = self.subst('$'+suffix)
1452 prefix = self.subst('$'+prefix)
1455 dir,name = os.path.split(str(path))
1456 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
1459 def ParseConfig(self, command, function=None, unique=1):
1461 Use the specified function to parse the output of the command
1462 in order to modify the current environment. The 'command' can
1463 be a string or a list of strings representing a command and
1464 its arguments. 'Function' is an optional argument that takes
1465 the environment, the output of the command, and the unique flag.
1466 If no function is specified, MergeFlags, which treats the output
1467 as the result of a typical 'X-config' command (i.e. gtk-config),
1468 will merge the output into the appropriate variables.
1470 if function is None:
1471 def parse_conf(env, cmd, unique=unique):
1472 return env.MergeFlags(cmd, unique)
1473 function = parse_conf
1474 if SCons.Util.is_List(command):
1475 command = string.join(command)
1476 command = self.subst(command)
1477 return function(self, self.backtick(command))
1479 def ParseDepends(self, filename, must_exist=None, only_one=0):
1481 Parse a mkdep-style file for explicit dependencies. This is
1482 completely abusable, and should be unnecessary in the "normal"
1483 case of proper SCons configuration, but it may help make
1484 the transition from a Make hierarchy easier for some people
1485 to swallow. It can also be genuinely useful when using a tool
1486 that can write a .d file, but for which writing a scanner would
1489 filename = self.subst(filename)
1491 fp = open(filename, 'r')
1496 lines = SCons.Util.LogicalLines(fp).readlines()
1497 lines = filter(lambda l: l[0] != '#', lines)
1501 target, depends = string.split(line, ':', 1)
1502 except (AttributeError, TypeError, ValueError):
1503 # Python 1.5.2 throws TypeError if line isn't a string,
1504 # Python 2.x throws AttributeError because it tries
1505 # to call line.split(). Either can throw ValueError
1506 # if the line doesn't split into two or more elements.
1509 tdlist.append((string.split(target), string.split(depends)))
1511 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
1512 if len(targets) > 1:
1513 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
1514 for target, depends in tdlist:
1515 self.Depends(target, depends)
1517 def Platform(self, platform):
1518 platform = self.subst(platform)
1519 return SCons.Platform.Platform(platform)(self)
1521 def Prepend(self, **kw):
1522 """Prepend values to existing construction variables
1525 kw = copy_non_reserved_keywords(kw)
1526 for key, val in kw.items():
1527 # It would be easier on the eyes to write this using
1528 # "continue" statements whenever we finish processing an item,
1529 # but Python 1.5.2 apparently doesn't let you use "continue"
1530 # within try:-except: blocks, so we have to nest our code.
1532 orig = self._dict[key]
1534 # No existing variable in the environment, so just set
1535 # it to the new value.
1536 self._dict[key] = val
1539 # Check if the original looks like a dictionary.
1540 # If it is, we can't just try adding the value because
1541 # dictionaries don't have __add__() methods, and
1542 # things like UserList will incorrectly coerce the
1543 # original dict to a list (which we don't want).
1544 update_dict = orig.update
1545 except AttributeError:
1547 # Most straightforward: just try to add them
1548 # together. This will work in most cases, when the
1549 # original and new values are of compatible types.
1550 self._dict[key] = val + orig
1551 except (KeyError, TypeError):
1553 # Check if the added value is a list.
1554 add_to_val = val.append
1555 except AttributeError:
1556 # The added value isn't a list, but the
1557 # original is (by process of elimination),
1558 # so insert the the new value in the original
1559 # (if there's one to insert).
1563 # The added value is a list, so append
1564 # the original to it (if there's a value
1568 self._dict[key] = val
1570 # The original looks like a dictionary, so update it
1571 # based on what we think the value looks like.
1572 if SCons.Util.is_List(val):
1578 except (AttributeError, TypeError, ValueError):
1579 if SCons.Util.is_Dict(val):
1580 for k, v in val.items():
1584 self.scanner_map_delete(kw)
1586 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep,
1588 """Prepend path elements to the path 'name' in the 'ENV'
1589 dictionary for this environment. Will only add any particular
1590 path once, and will normpath and normcase all paths to help
1591 assure this. This can also handle the case where the env
1592 variable is a list instead of a string.
1594 If delete_existing is 0, a newpath which is already in the path
1595 will not be moved to the front (it will be left where it is).
1599 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1600 orig = self._dict[envname][name]
1602 nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing,
1603 canonicalize=self._canonicalize)
1605 if not self._dict.has_key(envname):
1606 self._dict[envname] = {}
1608 self._dict[envname][name] = nv
1610 def PrependUnique(self, delete_existing=0, **kw):
1611 """Prepend values to existing construction variables
1612 in an Environment, if they're not already there.
1613 If delete_existing is 1, removes existing values first, so
1614 values move to front.
1616 kw = copy_non_reserved_keywords(kw)
1617 for key, val in kw.items():
1618 if SCons.Util.is_List(val):
1619 val = _delete_duplicates(val, not delete_existing)
1620 if not self._dict.has_key(key) or self._dict[key] in ('', None):
1621 self._dict[key] = val
1622 elif SCons.Util.is_Dict(self._dict[key]) and \
1623 SCons.Util.is_Dict(val):
1624 self._dict[key].update(val)
1625 elif SCons.Util.is_List(val):
1626 dk = self._dict[key]
1627 if not SCons.Util.is_List(dk):
1630 dk = filter(lambda x, val=val: x not in val, dk)
1632 val = filter(lambda x, dk=dk: x not in dk, val)
1633 self._dict[key] = val + dk
1635 dk = self._dict[key]
1636 if SCons.Util.is_List(dk):
1637 # By elimination, val is not a list. Since dk is a
1638 # list, wrap val in a list first.
1640 dk = filter(lambda x, val=val: x not in val, dk)
1641 self._dict[key] = [val] + dk
1644 self._dict[key] = [val] + dk
1647 dk = filter(lambda x, val=val: x not in val, dk)
1648 self._dict[key] = val + dk
1649 self.scanner_map_delete(kw)
1651 def Replace(self, **kw):
1652 """Replace existing construction variables in an Environment
1653 with new construction variables and/or values.
1656 kwbd = kw['BUILDERS']
1660 kwbd = semi_deepcopy(kwbd)
1662 self.__setitem__('BUILDERS', kwbd)
1663 kw = copy_non_reserved_keywords(kw)
1664 self._update(semi_deepcopy(kw))
1665 self.scanner_map_delete(kw)
1667 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1669 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1671 env - Environment used to interpolate variables.
1672 path - the path that will be modified.
1673 old_prefix - construction variable for the old prefix.
1674 old_suffix - construction variable for the old suffix.
1675 new_prefix - construction variable for the new prefix.
1676 new_suffix - construction variable for the new suffix.
1678 old_prefix = self.subst('$'+old_prefix)
1679 old_suffix = self.subst('$'+old_suffix)
1681 new_prefix = self.subst('$'+new_prefix)
1682 new_suffix = self.subst('$'+new_suffix)
1684 dir,name = os.path.split(str(path))
1685 if name[:len(old_prefix)] == old_prefix:
1686 name = name[len(old_prefix):]
1687 if name[-len(old_suffix):] == old_suffix:
1688 name = name[:-len(old_suffix)]
1689 return os.path.join(dir, new_prefix+name+new_suffix)
1691 def SetDefault(self, **kw):
1693 if self._dict.has_key(k):
1695 apply(self.Replace, (), kw)
1697 def _find_toolpath_dir(self, tp):
1698 return self.fs.Dir(self.subst(tp)).srcnode().abspath
1700 def Tool(self, tool, toolpath=None, **kw):
1701 if SCons.Util.is_String(tool):
1702 tool = self.subst(tool)
1703 if toolpath is None:
1704 toolpath = self.get('toolpath', [])
1705 toolpath = map(self._find_toolpath_dir, toolpath)
1706 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
1709 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1710 """Find prog in the path.
1714 path = self['ENV']['PATH']
1717 elif SCons.Util.is_String(path):
1718 path = self.subst(path)
1721 pathext = self['ENV']['PATHEXT']
1724 elif SCons.Util.is_String(pathext):
1725 pathext = self.subst(pathext)
1726 prog = self.subst(prog)
1727 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1728 if path: return path
1731 #######################################################################
1732 # Public methods for doing real "SCons stuff" (manipulating
1733 # dependencies, setting attributes on targets, etc.). These begin
1734 # with upper-case letters. The essential characteristic of methods
1735 # in this section is that they all *should* have corresponding
1736 # same-named global functions.
1737 #######################################################################
1739 def Action(self, *args, **kw):
1740 def subst_string(a, self=self):
1741 if SCons.Util.is_String(a):
1744 nargs = map(subst_string, args)
1745 nkw = self.subst_kw(kw)
1746 return apply(SCons.Action.Action, nargs, nkw)
1748 def AddPreAction(self, files, action):
1749 nodes = self.arg2nodes(files, self.fs.Entry)
1750 action = SCons.Action.Action(action)
1752 for executor in map(lambda n: n.get_executor(), nodes):
1754 for executor in uniq.keys():
1755 executor.add_pre_action(action)
1758 def AddPostAction(self, files, action):
1759 nodes = self.arg2nodes(files, self.fs.Entry)
1760 action = SCons.Action.Action(action)
1762 for executor in map(lambda n: n.get_executor(), nodes):
1764 for executor in uniq.keys():
1765 executor.add_post_action(action)
1768 def Alias(self, target, source=[], action=None, **kw):
1769 tlist = self.arg2nodes(target, self.ans.Alias)
1770 if not SCons.Util.is_List(source):
1772 source = filter(None, source)
1776 # There are no source files and no action, so just
1777 # return a target list of classic Alias Nodes, without
1778 # any builder. The externally visible effect is that
1779 # this will make the wrapping Script.BuildTask class
1780 # say that there's "Nothing to be done" for this Alias,
1781 # instead of that it's "up to date."
1784 # No action, but there are sources. Re-call all the target
1785 # builders to add the sources to each target.
1788 bld = t.get_builder(AliasBuilder)
1789 result.extend(bld(self, t, source))
1792 nkw = self.subst_kw(kw)
1794 'action' : SCons.Action.Action(action),
1795 'source_factory' : self.fs.Entry,
1797 'is_explicit' : None,
1799 bld = apply(SCons.Builder.Builder, (), nkw)
1801 # Apply the Builder separately to each target so that the Aliases
1802 # stay separate. If we did one "normal" Builder call with the
1803 # whole target list, then all of the target Aliases would be
1804 # associated under a single Executor.
1807 # Calling the convert() method will cause a new Executor to be
1808 # created from scratch, so we have to explicitly initialize
1809 # it with the target's existing sources, plus our new ones,
1810 # so nothing gets lost.
1812 if b is None or b is AliasBuilder:
1815 nkw['action'] = b.action + action
1816 b = apply(SCons.Builder.Builder, (), nkw)
1818 result.extend(b(self, t, t.sources + source))
1821 def AlwaysBuild(self, *targets):
1824 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1826 t.set_always_build()
1829 def BuildDir(self, *args, **kw):
1830 if kw.has_key('build_dir'):
1831 kw['variant_dir'] = kw['build_dir']
1833 return apply(self.VariantDir, args, kw)
1835 def Builder(self, **kw):
1836 nkw = self.subst_kw(kw)
1837 return apply(SCons.Builder.Builder, [], nkw)
1839 def CacheDir(self, path):
1840 import SCons.CacheDir
1841 if path is not None:
1842 path = self.subst(path)
1843 self._CacheDir_path = path
1845 def Clean(self, targets, files):
1847 tlist = self.arg2nodes(targets, self.fs.Entry)
1848 flist = self.arg2nodes(files, self.fs.Entry)
1851 CleanTargets[t].extend(flist)
1853 CleanTargets[t] = flist
1855 def Configure(self, *args, **kw):
1858 nargs = nargs + self.subst_list(args)[0]
1859 nkw = self.subst_kw(kw)
1860 nkw['_depth'] = kw.get('_depth', 0) + 1
1862 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1865 return apply(SCons.SConf.SConf, nargs, nkw)
1867 def Command(self, target, source, action, **kw):
1868 """Builds the supplied target files from the supplied
1869 source files using the supplied action. Action may
1870 be any type that the Builder constructor will accept
1874 'target_factory' : self.fs.Entry,
1875 'source_factory' : self.fs.Entry,
1877 try: bkw['source_scanner'] = kw['source_scanner']
1878 except KeyError: pass
1879 else: del kw['source_scanner']
1880 bld = apply(SCons.Builder.Builder, (), bkw)
1881 return apply(bld, (self, target, source), kw)
1883 def Depends(self, target, dependency):
1884 """Explicity specify that 'target's depend on 'dependency'."""
1885 tlist = self.arg2nodes(target, self.fs.Entry)
1886 dlist = self.arg2nodes(dependency, self.fs.Entry)
1888 t.add_dependency(dlist)
1891 def Dir(self, name, *args, **kw):
1894 s = self.subst(name)
1895 if SCons.Util.is_Sequence(s):
1898 result.append(apply(self.fs.Dir, (e,) + args, kw))
1900 return apply(self.fs.Dir, (s,) + args, kw)
1902 def NoClean(self, *targets):
1903 """Tags a target so that it will not be cleaned by -c"""
1906 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1911 def NoCache(self, *targets):
1912 """Tags a target so that it will not be cached"""
1915 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1920 def Entry(self, name, *args, **kw):
1923 s = self.subst(name)
1924 if SCons.Util.is_Sequence(s):
1927 result.append(apply(self.fs.Entry, (e,) + args, kw))
1929 return apply(self.fs.Entry, (s,) + args, kw)
1931 def Environment(self, **kw):
1932 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1934 def Execute(self, action, *args, **kw):
1935 """Directly execute an action through an Environment
1937 action = apply(self.Action, (action,) + args, kw)
1938 result = action([], [], self)
1939 if isinstance(result, SCons.Errors.BuildError):
1940 errstr = result.errstr
1942 errstr = result.filename + ': ' + errstr
1943 sys.stderr.write("scons: *** %s\n" % errstr)
1944 return result.status
1948 def File(self, name, *args, **kw):
1951 s = self.subst(name)
1952 if SCons.Util.is_Sequence(s):
1955 result.append(apply(self.fs.File, (e,) + args, kw))
1957 return apply(self.fs.File, (s,) + args, kw)
1959 def FindFile(self, file, dirs):
1960 file = self.subst(file)
1961 nodes = self.arg2nodes(dirs, self.fs.Dir)
1962 return SCons.Node.FS.find_file(file, tuple(nodes))
1964 def Flatten(self, sequence):
1965 return SCons.Util.flatten(sequence)
1967 def GetBuildPath(self, files):
1968 result = map(str, self.arg2nodes(files, self.fs.Entry))
1969 if SCons.Util.is_List(files):
1974 def Glob(self, pattern, ondisk=True, source=False, strings=False):
1975 return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
1977 def Ignore(self, target, dependency):
1978 """Ignore a dependency."""
1979 tlist = self.arg2nodes(target, self.fs.Entry)
1980 dlist = self.arg2nodes(dependency, self.fs.Entry)
1985 def Literal(self, string):
1986 return SCons.Subst.Literal(string)
1988 def Local(self, *targets):
1990 for targ in targets:
1991 if isinstance(targ, SCons.Node.Node):
1995 for t in self.arg2nodes(targ, self.fs.Entry):
2000 def Precious(self, *targets):
2003 tlist.extend(self.arg2nodes(t, self.fs.Entry))
2008 def Repository(self, *dirs, **kw):
2009 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
2010 apply(self.fs.Repository, dirs, kw)
2012 def Requires(self, target, prerequisite):
2013 """Specify that 'prerequisite' must be built before 'target',
2014 (but 'target' does not actually depend on 'prerequisite'
2015 and need not be rebuilt if it changes)."""
2016 tlist = self.arg2nodes(target, self.fs.Entry)
2017 plist = self.arg2nodes(prerequisite, self.fs.Entry)
2019 t.add_prerequisite(plist)
2022 def Scanner(self, *args, **kw):
2025 if SCons.Util.is_String(arg):
2026 arg = self.subst(arg)
2028 nkw = self.subst_kw(kw)
2029 return apply(SCons.Scanner.Base, nargs, nkw)
2031 def SConsignFile(self, name=".sconsign", dbm_module=None):
2032 if name is not None:
2033 name = self.subst(name)
2034 if not os.path.isabs(name):
2035 name = os.path.join(str(self.fs.SConstruct_dir), name)
2037 name = os.path.normpath(name)
2038 sconsign_dir = os.path.dirname(name)
2039 if sconsign_dir and not os.path.exists(sconsign_dir):
2040 self.Execute(SCons.Defaults.Mkdir(sconsign_dir))
2041 SCons.SConsign.File(name, dbm_module)
2043 def SideEffect(self, side_effect, target):
2044 """Tell scons that side_effects are built as side
2045 effects of building targets."""
2046 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
2047 targets = self.arg2nodes(target, self.fs.Entry)
2049 for side_effect in side_effects:
2050 if side_effect.multiple_side_effect_has_builder():
2051 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
2052 side_effect.add_source(targets)
2053 side_effect.side_effect = 1
2054 self.Precious(side_effect)
2055 for target in targets:
2056 target.side_effects.append(side_effect)
2059 def SourceCode(self, entry, builder):
2060 """Arrange for a source code builder for (part of) a tree."""
2061 entries = self.arg2nodes(entry, self.fs.Entry)
2062 for entry in entries:
2063 entry.set_src_builder(builder)
2066 def SourceSignatures(self, type):
2067 global _warn_source_signatures_deprecated
2068 if _warn_source_signatures_deprecated:
2069 msg = "The env.SourceSignatures() method is deprecated;\n" + \
2070 "\tconvert your build to use the env.Decider() method instead."
2071 SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
2072 _warn_source_signatures_deprecated = False
2073 type = self.subst(type)
2074 self.src_sig_type = type
2076 if not SCons.Util.md5:
2077 raise UserError, "MD5 signatures are not available in this version of Python."
2078 self.decide_source = self._changed_content
2079 elif type == 'timestamp':
2080 self.decide_source = self._changed_timestamp_match
2082 raise UserError, "Unknown source signature type '%s'" % type
2084 def Split(self, arg):
2085 """This function converts a string or list into a list of strings
2086 or Nodes. This makes things easier for users by allowing files to
2087 be specified as a white-space separated list to be split.
2088 The input rules are:
2089 - A single string containing names separated by spaces. These will be
2090 split apart at the spaces.
2091 - A single Node instance
2092 - A list containing either strings or Node instances. Any strings
2093 in the list are not split at spaces.
2094 In all cases, the function returns a list of Nodes and strings."""
2095 if SCons.Util.is_List(arg):
2096 return map(self.subst, arg)
2097 elif SCons.Util.is_String(arg):
2098 return string.split(self.subst(arg))
2100 return [self.subst(arg)]
2102 def TargetSignatures(self, type):
2103 global _warn_target_signatures_deprecated
2104 if _warn_target_signatures_deprecated:
2105 msg = "The env.TargetSignatures() method is deprecated;\n" + \
2106 "\tconvert your build to use the env.Decider() method instead."
2107 SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
2108 _warn_target_signatures_deprecated = False
2109 type = self.subst(type)
2110 self.tgt_sig_type = type
2111 if type in ('MD5', 'content'):
2112 if not SCons.Util.md5:
2113 raise UserError, "MD5 signatures are not available in this version of Python."
2114 self.decide_target = self._changed_content
2115 elif type == 'timestamp':
2116 self.decide_target = self._changed_timestamp_match
2117 elif type == 'build':
2118 self.decide_target = self._changed_build
2119 elif type == 'source':
2120 self.decide_target = self._changed_source
2122 raise UserError, "Unknown target signature type '%s'"%type
2124 def Value(self, value, built_value=None):
2127 return SCons.Node.Python.Value(value, built_value)
2129 def VariantDir(self, variant_dir, src_dir, duplicate=1):
2130 variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
2131 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
2132 self.fs.VariantDir(variant_dir, src_dir, duplicate)
2134 def FindSourceFiles(self, node='.'):
2135 """ returns a list of all source files.
2137 node = self.arg2nodes(node, self.fs.Entry)[0]
2140 # Uncomment this and get rid of the global definition when we
2141 # drop support for pre-2.2 Python versions.
2142 #def build_source(ss, result):
2144 # if isinstance(s, SCons.Node.FS.Dir):
2145 # build_source(s.all_children(), result)
2146 # elif s.has_builder():
2147 # build_source(s.sources, result)
2148 # elif isinstance(s.disambiguate(), SCons.Node.FS.File):
2150 build_source(node.all_children(), sources)
2152 # THIS CODE APPEARS TO HAVE NO EFFECT
2153 # # get the final srcnode for all nodes, this means stripping any
2154 # # attached build node by calling the srcnode function
2155 # for file in sources:
2156 # srcnode = file.srcnode()
2157 # while srcnode != file.srcnode():
2158 # srcnode = file.srcnode()
2161 return list(set(sources))
2163 def FindInstalledFiles(self):
2164 """ returns the list of all targets of the Install and InstallAs Builder.
2166 from SCons.Tool import install
2167 if install._UNIQUE_INSTALLED_FILES is None:
2168 install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
2169 return install._UNIQUE_INSTALLED_FILES
2171 class OverrideEnvironment(Base):
2172 """A proxy that overrides variables in a wrapped construction
2173 environment by returning values from an overrides dictionary in
2174 preference to values from the underlying subject environment.
2176 This is a lightweight (I hope) proxy that passes through most use of
2177 attributes to the underlying Environment.Base class, but has just
2178 enough additional methods defined to act like a real construction
2179 environment with overridden values. It can wrap either a Base
2180 construction environment, or another OverrideEnvironment, which
2181 can in turn nest arbitrary OverrideEnvironments...
2183 Note that we do *not* call the underlying base class
2184 (SubsitutionEnvironment) initialization, because we get most of those
2185 from proxying the attributes of the subject construction environment.
2186 But because we subclass SubstitutionEnvironment, this class also
2187 has inherited arg2nodes() and subst*() methods; those methods can't
2188 be proxied because they need *this* object's methods to fetch the
2189 values from the overrides dictionary.
2192 def __init__(self, subject, overrides={}):
2193 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
2194 self.__dict__['__subject'] = subject
2195 self.__dict__['overrides'] = overrides
2197 # Methods that make this class act like a proxy.
2198 def __getattr__(self, name):
2199 return getattr(self.__dict__['__subject'], name)
2200 def __setattr__(self, name, value):
2201 setattr(self.__dict__['__subject'], name, value)
2203 # Methods that make this class act like a dictionary.
2204 def __getitem__(self, key):
2206 return self.__dict__['overrides'][key]
2208 return self.__dict__['__subject'].__getitem__(key)
2209 def __setitem__(self, key, value):
2210 if not is_valid_construction_var(key):
2211 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
2212 self.__dict__['overrides'][key] = value
2213 def __delitem__(self, key):
2215 del self.__dict__['overrides'][key]
2221 result = self.__dict__['__subject'].__delitem__(key)
2227 def get(self, key, default=None):
2228 """Emulates the get() method of dictionaries."""
2230 return self.__dict__['overrides'][key]
2232 return self.__dict__['__subject'].get(key, default)
2233 def has_key(self, key):
2235 self.__dict__['overrides'][key]
2238 return self.__dict__['__subject'].has_key(key)
2239 def __contains__(self, key):
2240 if self.__dict__['overrides'].__contains__(key):
2242 return self.__dict__['__subject'].__contains__(key)
2243 def Dictionary(self):
2244 """Emulates the items() method of dictionaries."""
2245 d = self.__dict__['__subject'].Dictionary().copy()
2246 d.update(self.__dict__['overrides'])
2249 """Emulates the items() method of dictionaries."""
2250 return self.Dictionary().items()
2252 # Overridden private construction environment methods.
2253 def _update(self, dict):
2254 """Update an environment's values directly, bypassing the normal
2255 checks that occur when users try to set items.
2257 self.__dict__['overrides'].update(dict)
2260 return self.__dict__['__subject'].gvars()
2263 lvars = self.__dict__['__subject'].lvars()
2264 lvars.update(self.__dict__['overrides'])
2267 # Overridden public construction environment methods.
2268 def Replace(self, **kw):
2269 kw = copy_non_reserved_keywords(kw)
2270 self.__dict__['overrides'].update(semi_deepcopy(kw))
2272 # The entry point that will be used by the external world
2273 # to refer to a construction environment. This allows the wrapper
2274 # interface to extend a construction environment for its own purposes
2275 # by subclassing SCons.Environment.Base and then assigning the
2276 # class to SCons.Environment.Environment.
2280 # An entry point for returning a proxy subclass instance that overrides
2281 # the subst*() methods so they don't actually perform construction
2282 # variable substitution. This is specifically intended to be the shim
2283 # layer in between global function calls (which don't want construction
2284 # variable substitution) and the DefaultEnvironment() (which would
2285 # substitute variables if left to its own devices)."""
2287 # We have to wrap this in a function that allows us to delay definition of
2288 # the class until it's necessary, so that when it subclasses Environment
2289 # it will pick up whatever Environment subclass the wrapper interface
2290 # might have assigned to SCons.Environment.Environment.
2292 def NoSubstitutionProxy(subject):
2293 class _NoSubstitutionProxy(Environment):
2294 def __init__(self, subject):
2295 self.__dict__['__subject'] = subject
2296 def __getattr__(self, name):
2297 return getattr(self.__dict__['__subject'], name)
2298 def __setattr__(self, name, value):
2299 return setattr(self.__dict__['__subject'], name, value)
2300 def raw_to_mode(self, dict):
2308 def subst(self, string, *args, **kwargs):
2310 def subst_kw(self, kw, *args, **kwargs):
2312 def subst_list(self, string, *args, **kwargs):
2313 nargs = (string, self,) + args
2316 self.raw_to_mode(nkw)
2317 return apply(SCons.Subst.scons_subst_list, nargs, nkw)
2318 def subst_target_source(self, string, *args, **kwargs):
2319 nargs = (string, self,) + args
2322 self.raw_to_mode(nkw)
2323 return apply(SCons.Subst.scons_subst, nargs, nkw)
2324 return _NoSubstitutionProxy(subject)
2328 # indent-tabs-mode:nil
2330 # vim: set expandtab tabstop=4 shiftwidth=4: