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.
34 from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
36 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
44 from collections 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 [_f for _f in tools if _f]:
102 if SCons.Util.is_List(tool) or isinstance(tool, tuple):
104 toolargs = tool[1] # should be a dict of kw args
105 tool = 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
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 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 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 # 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 key not in self._dict \
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 key in self._dict
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 = 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(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 = ''.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 = SCons.Action._subproc(self, command, **kw)
591 out,err = p.communicate()
594 sys.stderr.write(unicode(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 self.added_methods = [dm for dm in self.added_methods if not dm.method is function]
615 def Override(self, overrides):
617 Produce a modified environment whose variables are overriden by
618 the overrides dictionaries. "overrides" is a dictionary that
619 will override the variables of this environment.
621 This function is much more efficient than Clone() or creating
622 a new Environment because it doesn't copy the construction
623 environment dictionary, it just wraps the underlying construction
624 environment, and doesn't even create a wrapper object if there
627 if not overrides: return self
628 o = copy_non_reserved_keywords(overrides)
629 if not o: return self
632 for key, value in o.items():
633 if key == 'parse_flags':
636 overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
637 env = OverrideEnvironment(self, overrides)
638 if merges: env.MergeFlags(merges)
641 def ParseFlags(self, *flags):
643 Parse the set of flags and return a dict with the flags placed
644 in the appropriate entry. The flags are treated as a typical
645 set of command-line flags for a GNU-like toolchain and used to
646 populate the entries in the dict immediately below. If one of
647 the flag strings begins with a bang (exclamation mark), it is
648 assumed to be a command and the rest of the string is executed;
649 the result of that evaluation is then added to the dict.
652 'ASFLAGS' : SCons.Util.CLVar(''),
653 'CFLAGS' : SCons.Util.CLVar(''),
654 'CCFLAGS' : SCons.Util.CLVar(''),
656 'CPPFLAGS' : SCons.Util.CLVar(''),
658 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
659 'FRAMEWORKS' : SCons.Util.CLVar(''),
662 'LINKFLAGS' : SCons.Util.CLVar(''),
666 # The use of the "me" parameter to provide our own name for
667 # recursion is an egregious hack to support Python 2.1 and before.
668 def do_parse(arg, me, self = self, dict = dict):
669 # if arg is a sequence, recurse with each element
673 if not SCons.Util.is_String(arg):
674 for t in arg: me(t, me)
677 # if arg is a command, execute it
679 arg = self.backtick(arg[1:])
681 # utility function to deal with -D option
682 def append_define(name, dict = dict):
685 dict['CPPDEFINES'].append(name)
687 dict['CPPDEFINES'].append([t[0], '='.join(t[1:])])
689 # Loop through the flags and add them to the appropriate option.
690 # This tries to strike a balance between checking for all possible
691 # flags and keeping the logic to a finite size, so it doesn't
692 # check for some that don't occur often. It particular, if the
693 # flag is not known to occur in a config script and there's a way
694 # of passing the flag to the right place (by wrapping it in a -W
695 # flag, for example) we don't check for it. Note that most
696 # preprocessor options are not handled, since unhandled options
697 # are placed in CCFLAGS, so unless the preprocessor is invoked
698 # separately, these flags will still get to the preprocessor.
699 # Other options not currently handled:
700 # -iqoutedir (preprocessor search path)
701 # -u symbol (linker undefined symbol)
702 # -s (linker strip files)
703 # -static* (linker static binding)
704 # -shared* (linker dynamic binding)
705 # -symbolic (linker global binding)
706 # -R dir (deprecated linker rpath)
707 # IBM compilers may also accept -qframeworkdir=foo
709 params = shlex.split(arg)
710 append_next_arg_to = None # for multi-word args
712 if append_next_arg_to:
713 if append_next_arg_to == 'CPPDEFINES':
715 elif append_next_arg_to == '-include':
716 t = ('-include', self.fs.File(arg))
717 dict['CCFLAGS'].append(t)
718 elif append_next_arg_to == '-isysroot':
719 t = ('-isysroot', arg)
720 dict['CCFLAGS'].append(t)
721 dict['LINKFLAGS'].append(t)
722 elif append_next_arg_to == '-arch':
724 dict['CCFLAGS'].append(t)
725 dict['LINKFLAGS'].append(t)
727 dict[append_next_arg_to].append(arg)
728 append_next_arg_to = None
729 elif not arg[0] in ['-', '+']:
730 dict['LIBS'].append(self.fs.File(arg))
731 elif arg[:2] == '-L':
733 dict['LIBPATH'].append(arg[2:])
735 append_next_arg_to = 'LIBPATH'
736 elif arg[:2] == '-l':
738 dict['LIBS'].append(arg[2:])
740 append_next_arg_to = 'LIBS'
741 elif arg[:2] == '-I':
743 dict['CPPPATH'].append(arg[2:])
745 append_next_arg_to = 'CPPPATH'
746 elif arg[:4] == '-Wa,':
747 dict['ASFLAGS'].append(arg[4:])
748 dict['CCFLAGS'].append(arg)
749 elif arg[:4] == '-Wl,':
750 if arg[:11] == '-Wl,-rpath=':
751 dict['RPATH'].append(arg[11:])
752 elif arg[:7] == '-Wl,-R,':
753 dict['RPATH'].append(arg[7:])
754 elif arg[:6] == '-Wl,-R':
755 dict['RPATH'].append(arg[6:])
757 dict['LINKFLAGS'].append(arg)
758 elif arg[:4] == '-Wp,':
759 dict['CPPFLAGS'].append(arg)
760 elif arg[:2] == '-D':
762 append_define(arg[2:])
764 append_next_arg_to = 'CPPDEFINES'
765 elif arg == '-framework':
766 append_next_arg_to = 'FRAMEWORKS'
767 elif arg[:14] == '-frameworkdir=':
768 dict['FRAMEWORKPATH'].append(arg[14:])
769 elif arg[:2] == '-F':
771 dict['FRAMEWORKPATH'].append(arg[2:])
773 append_next_arg_to = 'FRAMEWORKPATH'
774 elif arg == '-mno-cygwin':
775 dict['CCFLAGS'].append(arg)
776 dict['LINKFLAGS'].append(arg)
777 elif arg == '-mwindows':
778 dict['LINKFLAGS'].append(arg)
779 elif arg == '-pthread':
780 dict['CCFLAGS'].append(arg)
781 dict['LINKFLAGS'].append(arg)
782 elif arg[:5] == '-std=':
783 dict['CFLAGS'].append(arg) # C only
785 dict['CCFLAGS'].append(arg)
786 dict['LINKFLAGS'].append(arg)
787 elif arg in ['-include', '-isysroot', '-arch']:
788 append_next_arg_to = arg
790 dict['CCFLAGS'].append(arg)
793 do_parse(arg, do_parse)
796 def MergeFlags(self, args, unique=1, dict=None):
798 Merge the dict in args into the construction variables of this
799 env, or the passed-in dict. If args is not a dict, it is
800 converted into a dict using ParseFlags. If unique is not set,
801 the flags are appended rather than merged.
806 if not SCons.Util.is_Dict(args):
807 args = self.ParseFlags(args)
811 for key, value in args.items():
822 # Add orig and value. The logic here was lifted from
823 # part of env.Append() (see there for a lot of comments
824 # about the order in which things are tried) and is
825 # used mainly to handle coercion of strings to CLVar to
826 # "do the right thing" given (e.g.) an original CCFLAGS
827 # string variable like '-pipe -Wall'.
830 except (KeyError, TypeError):
832 add_to_orig = orig.append
833 except AttributeError:
834 value.insert(0, orig)
839 if key[-4:] == 'PATH':
840 ### keep left-most occurence
845 ### keep right-most occurence
853 # def MergeShellPaths(self, args, prepend=1):
855 # Merge the dict in args into the shell environment in env['ENV'].
856 # Shell path elements are appended or prepended according to prepend.
858 # Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
860 # Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'})
861 # prepends /usr/local/lib to env['ENV']['LIBPATH'].
864 # for pathname, pathval in args.items():
868 # self.PrependENVPath(pathname, pathval)
870 # self.AppendENVPath(pathname, pathval)
873 # Used by the FindSourceFiles() method, below.
874 # Stuck here for support of pre-2.2 Python versions.
875 def build_source(ss, result):
877 if isinstance(s, SCons.Node.FS.Dir):
878 build_source(s.all_children(), result)
879 elif s.has_builder():
880 build_source(s.sources, result)
881 elif isinstance(s.disambiguate(), SCons.Node.FS.File):
884 def default_decide_source(dependency, target, prev_ni):
885 f = SCons.Defaults.DefaultEnvironment().decide_source
886 return f(dependency, target, prev_ni)
888 def default_decide_target(dependency, target, prev_ni):
889 f = SCons.Defaults.DefaultEnvironment().decide_target
890 return f(dependency, target, prev_ni)
892 def default_copy_from_cache(src, dst):
893 f = SCons.Defaults.DefaultEnvironment().copy_from_cache
896 class Base(SubstitutionEnvironment):
897 """Base class for "real" construction Environments. These are the
898 primary objects used to communicate dependency and construction
899 information to the build engine.
901 Keyword arguments supplied when the construction Environment
902 is created are construction variables used to initialize the
906 memoizer_counters = []
908 #######################################################################
909 # This is THE class for interacting with the SCons build engine,
910 # and it contains a lot of stuff, so we're going to try to keep this
911 # a little organized by grouping the methods.
912 #######################################################################
914 #######################################################################
915 # Methods that make an Environment act like a dictionary. These have
916 # the expected standard names for Python mapping objects. Note that
917 # we don't actually make an Environment a subclass of UserDict for
918 # performance reasons. Note also that we only supply methods for
919 # dictionary functionality that we actually need and use.
920 #######################################################################
930 Initialization of a basic SCons construction environment,
931 including setting up special construction variables like BUILDER,
932 PLATFORM, etc., and searching for and applying available Tools.
934 Note that we do *not* call the underlying base class
935 (SubsitutionEnvironment) initialization, because we need to
936 initialize things in a very specific order that doesn't work
937 with the much simpler base class initialization.
939 if __debug__: logInstanceCreation(self, 'Environment.Base')
941 self.fs = SCons.Node.FS.get_default_fs()
942 self.ans = SCons.Node.Alias.default_ans
943 self.lookup_list = SCons.Node.arg2nodes_lookups
944 self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment)
946 self.added_methods = []
948 # We don't use AddMethod, or define these as methods in this
949 # class, because we *don't* want these functions to be bound
950 # methods. They need to operate independently so that the
951 # settings will work properly regardless of whether a given
952 # target ends up being built with a Base environment or an
953 # OverrideEnvironment or what have you.
954 self.decide_target = default_decide_target
955 self.decide_source = default_decide_source
957 self.copy_from_cache = default_copy_from_cache
959 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
962 platform = self._dict.get('PLATFORM', None)
964 platform = SCons.Platform.Platform()
965 if SCons.Util.is_String(platform):
966 platform = SCons.Platform.Platform(platform)
967 self._dict['PLATFORM'] = str(platform)
970 self._dict['HOST_OS'] = self._dict.get('HOST_OS',None)
971 self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None)
973 # Now set defaults for TARGET_{OS|ARCH}
974 self._dict['TARGET_OS'] = self._dict.get('HOST_OS',None)
975 self._dict['TARGET_ARCH'] = self._dict.get('HOST_ARCH',None)
978 # Apply the passed-in and customizable variables to the
979 # environment before calling the tools, because they may use
980 # some of them during initialization.
982 # Backwards compatibility: they may stll be using the
983 # old "options" keyword.
984 variables = kw['options']
989 keys = keys + variables.keys()
990 variables.Update(self)
995 save[k] = self._dict[k]
997 # No value may have been set if they tried to pass in a
998 # reserved variable name like TARGETS.
1001 SCons.Tool.Initializers(self)
1004 tools = self._dict.get('TOOLS', None)
1007 apply_tools(self, tools, toolpath)
1009 # Now restore the passed-in and customized variables
1010 # to the environment, since the values the user set explicitly
1011 # should override any values set by the tools.
1012 for key, val in save.items():
1013 self._dict[key] = val
1015 # Finally, apply any flags to be merged in
1016 if parse_flags: self.MergeFlags(parse_flags)
1018 #######################################################################
1019 # Utility methods that are primarily for internal use by SCons.
1020 # These begin with lower-case letters.
1021 #######################################################################
1023 def get_builder(self, name):
1024 """Fetch the builder with the specified name from the environment.
1027 return self._dict['BUILDERS'][name]
1031 def get_CacheDir(self):
1033 path = self._CacheDir_path
1034 except AttributeError:
1035 path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
1037 if path == self._last_CacheDir_path:
1038 return self._last_CacheDir
1039 except AttributeError:
1041 cd = SCons.CacheDir.CacheDir(path)
1042 self._last_CacheDir_path = path
1043 self._last_CacheDir = cd
1046 def get_factory(self, factory, default='File'):
1047 """Return a factory function for creating Nodes for this
1048 construction environment.
1052 is_node = issubclass(factory, SCons.Node.FS.Base)
1054 # The specified factory isn't a Node itself--it's
1055 # most likely None, or possibly a callable.
1059 # The specified factory is a Node (sub)class. Try to
1060 # return the FS method that corresponds to the Node's
1061 # name--that is, we return self.fs.Dir if they want a Dir,
1062 # self.fs.File for a File, etc.
1063 try: name = factory.__name__
1064 except AttributeError: pass
1065 else: factory = None
1067 # They passed us None, or we picked up a name from a specified
1068 # class, so return the FS method. (Note that we *don't*
1069 # use our own self.{Dir,File} methods because that would
1070 # cause env.subst() to be called twice on the file name,
1071 # interfering with files that have $$ in them.)
1072 factory = getattr(self.fs, name)
1075 memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
1079 return self._memo['_gsm']
1086 scanners = self._dict['SCANNERS']
1090 # Reverse the scanner list so that, if multiple scanners
1091 # claim they can scan the same suffix, earlier scanners
1092 # in the list will overwrite later scanners, so that
1093 # the result looks like a "first match" to the user.
1094 if not SCons.Util.is_List(scanners):
1095 scanners = [scanners]
1097 scanners = scanners[:] # copy so reverse() doesn't mod original
1099 for scanner in scanners:
1100 for k in scanner.get_skeys(self):
1101 if k and self['PLATFORM'] == 'win32':
1105 self._memo['_gsm'] = result
1109 def get_scanner(self, skey):
1110 """Find the appropriate scanner given a key (usually a file suffix).
1112 if skey and self['PLATFORM'] == 'win32':
1114 return self._gsm().get(skey)
1116 def scanner_map_delete(self, kw=None):
1117 """Delete the cached scanner map (if we need to).
1120 del self._memo['_gsm']
1124 def _update(self, dict):
1125 """Update an environment's values directly, bypassing the normal
1126 checks that occur when users try to set items.
1128 self._dict.update(dict)
1130 def get_src_sig_type(self):
1132 return self.src_sig_type
1133 except AttributeError:
1134 t = SCons.Defaults.DefaultEnvironment().src_sig_type
1135 self.src_sig_type = t
1138 def get_tgt_sig_type(self):
1140 return self.tgt_sig_type
1141 except AttributeError:
1142 t = SCons.Defaults.DefaultEnvironment().tgt_sig_type
1143 self.tgt_sig_type = t
1146 #######################################################################
1147 # Public methods for manipulating an Environment. These begin with
1148 # upper-case letters. The essential characteristic of methods in
1149 # this section is that they do *not* have corresponding same-named
1150 # global functions. For example, a stand-alone Append() function
1151 # makes no sense, because Append() is all about appending values to
1152 # an Environment's construction variables.
1153 #######################################################################
1155 def Append(self, **kw):
1156 """Append values to existing construction variables
1159 kw = copy_non_reserved_keywords(kw)
1160 for key, val in kw.items():
1161 # It would be easier on the eyes to write this using
1162 # "continue" statements whenever we finish processing an item,
1163 # but Python 1.5.2 apparently doesn't let you use "continue"
1164 # within try:-except: blocks, so we have to nest our code.
1166 orig = self._dict[key]
1168 # No existing variable in the environment, so just set
1169 # it to the new value.
1170 self._dict[key] = val
1173 # Check if the original looks like a dictionary.
1174 # If it is, we can't just try adding the value because
1175 # dictionaries don't have __add__() methods, and
1176 # things like UserList will incorrectly coerce the
1177 # original dict to a list (which we don't want).
1178 update_dict = orig.update
1179 except AttributeError:
1181 # Most straightforward: just try to add them
1182 # together. This will work in most cases, when the
1183 # original and new values are of compatible types.
1184 self._dict[key] = orig + val
1185 except (KeyError, TypeError):
1187 # Check if the original is a list.
1188 add_to_orig = orig.append
1189 except AttributeError:
1190 # The original isn't a list, but the new
1191 # value is (by process of elimination),
1192 # so insert the original in the new value
1193 # (if there's one to insert) and replace
1194 # the variable with it.
1197 self._dict[key] = val
1199 # The original is a list, so append the new
1200 # value to it (if there's a value to append).
1204 # The original looks like a dictionary, so update it
1205 # based on what we think the value looks like.
1206 if SCons.Util.is_List(val):
1212 except (AttributeError, TypeError, ValueError):
1213 if SCons.Util.is_Dict(val):
1214 for k, v in val.items():
1218 self.scanner_map_delete(kw)
1220 # allow Dirs and strings beginning with # for top-relative
1221 # Note this uses the current env's fs (in self).
1222 def _canonicalize(self, path):
1223 if not SCons.Util.is_String(path): # typically a Dir
1225 if path and path[0] == '#':
1226 path = str(self.fs.Dir(path))
1229 def AppendENVPath(self, name, newpath, envname = 'ENV',
1230 sep = os.pathsep, delete_existing=1):
1231 """Append path elements to the path 'name' in the 'ENV'
1232 dictionary for this environment. Will only add any particular
1233 path once, and will normpath and normcase all paths to help
1234 assure this. This can also handle the case where the env
1235 variable is a list instead of a string.
1237 If delete_existing is 0, a newpath which is already in the path
1238 will not be moved to the end (it will be left where it is).
1242 if envname in self._dict and name in self._dict[envname]:
1243 orig = self._dict[envname][name]
1245 nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing,
1246 canonicalize=self._canonicalize)
1248 if envname not in self._dict:
1249 self._dict[envname] = {}
1251 self._dict[envname][name] = nv
1253 def AppendUnique(self, delete_existing=0, **kw):
1254 """Append values to existing construction variables
1255 in an Environment, if they're not already there.
1256 If delete_existing is 1, removes existing values first, so
1259 kw = copy_non_reserved_keywords(kw)
1260 for key, val in kw.items():
1261 if SCons.Util.is_List(val):
1262 val = _delete_duplicates(val, delete_existing)
1263 if key not in self._dict or self._dict[key] in ('', None):
1264 self._dict[key] = val
1265 elif SCons.Util.is_Dict(self._dict[key]) and \
1266 SCons.Util.is_Dict(val):
1267 self._dict[key].update(val)
1268 elif SCons.Util.is_List(val):
1269 dk = self._dict[key]
1270 if not SCons.Util.is_List(dk):
1273 dk = [x for x in dk if x not in val]
1275 val = [x for x in val if x not in dk]
1276 self._dict[key] = dk + val
1278 dk = self._dict[key]
1279 if SCons.Util.is_List(dk):
1280 # By elimination, val is not a list. Since dk is a
1281 # list, wrap val in a list first.
1283 dk = [x for x in dk if x not in val]
1284 self._dict[key] = dk + [val]
1287 self._dict[key] = dk + [val]
1290 dk = [x for x in dk if x not in val]
1291 self._dict[key] = dk + val
1292 self.scanner_map_delete(kw)
1294 def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
1295 """Return a copy of a construction Environment. The
1296 copy is like a Python "deep copy"--that is, independent
1297 copies are made recursively of each objects--except that
1298 a reference is copied when an object is not deep-copyable
1299 (like a function). There are no references to any mutable
1300 objects in the original Environment.
1302 clone = copy.copy(self)
1303 clone._dict = semi_deepcopy(self._dict)
1306 cbd = clone._dict['BUILDERS']
1310 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
1312 # Check the methods added via AddMethod() and re-bind them to
1313 # the cloned environment. Only do this if the attribute hasn't
1314 # been overwritten by the user explicitly and still points to
1316 clone.added_methods = []
1317 for mw in self.added_methods:
1318 if mw == getattr(self, mw.name):
1319 clone.added_methods.append(mw.clone(clone))
1323 # Apply passed-in variables before the tools
1324 # so the tools can use the new variables
1325 kw = copy_non_reserved_keywords(kw)
1327 for key, value in kw.items():
1328 new[key] = SCons.Subst.scons_subst_once(value, self, key)
1329 clone.Replace(**new)
1331 apply_tools(clone, tools, toolpath)
1333 # apply them again in case the tools overwrote them
1334 clone.Replace(**new)
1336 # Finally, apply any flags to be merged in
1337 if parse_flags: clone.MergeFlags(parse_flags)
1339 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
1342 def Copy(self, *args, **kw):
1343 global _warn_copy_deprecated
1344 if _warn_copy_deprecated:
1345 msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
1346 SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
1347 _warn_copy_deprecated = False
1348 return self.Clone(*args, **kw)
1350 def _changed_build(self, dependency, target, prev_ni):
1351 if dependency.changed_state(target, prev_ni):
1353 return self.decide_source(dependency, target, prev_ni)
1355 def _changed_content(self, dependency, target, prev_ni):
1356 return dependency.changed_content(target, prev_ni)
1358 def _changed_source(self, dependency, target, prev_ni):
1359 target_env = dependency.get_build_env()
1360 type = target_env.get_tgt_sig_type()
1361 if type == 'source':
1362 return target_env.decide_source(dependency, target, prev_ni)
1364 return target_env.decide_target(dependency, target, prev_ni)
1366 def _changed_timestamp_then_content(self, dependency, target, prev_ni):
1367 return dependency.changed_timestamp_then_content(target, prev_ni)
1369 def _changed_timestamp_newer(self, dependency, target, prev_ni):
1370 return dependency.changed_timestamp_newer(target, prev_ni)
1372 def _changed_timestamp_match(self, dependency, target, prev_ni):
1373 return dependency.changed_timestamp_match(target, prev_ni)
1375 def _copy_from_cache(self, src, dst):
1376 return self.fs.copy(src, dst)
1378 def _copy2_from_cache(self, src, dst):
1379 return self.fs.copy2(src, dst)
1381 def Decider(self, function):
1382 copy_function = self._copy2_from_cache
1383 if function in ('MD5', 'content'):
1384 if not SCons.Util.md5:
1385 raise UserError("MD5 signatures are not available in this version of Python.")
1386 function = self._changed_content
1387 elif function == 'MD5-timestamp':
1388 function = self._changed_timestamp_then_content
1389 elif function in ('timestamp-newer', 'make'):
1390 function = self._changed_timestamp_newer
1391 copy_function = self._copy_from_cache
1392 elif function == 'timestamp-match':
1393 function = self._changed_timestamp_match
1394 elif not callable(function):
1395 raise UserError("Unknown Decider value %s" % repr(function))
1397 # We don't use AddMethod because we don't want to turn the
1398 # function, which only expects three arguments, into a bound
1399 # method, which would add self as an initial, fourth argument.
1400 self.decide_target = function
1401 self.decide_source = function
1403 self.copy_from_cache = copy_function
1405 def Detect(self, progs):
1406 """Return the first available program in progs.
1408 if not SCons.Util.is_List(progs):
1411 path = self.WhereIs(prog)
1412 if path: return prog
1415 def Dictionary(self, *args):
1418 dlist = [self._dict[x] for x in args]
1423 def Dump(self, key = None):
1425 Using the standard Python pretty printer, dump the contents of the
1426 scons build environment to stdout.
1428 If the key passed in is anything other than None, then that will
1429 be used as an index into the build environment dictionary and
1430 whatever is found there will be fed into the pretty printer. Note
1431 that this key is case sensitive.
1434 pp = pprint.PrettyPrinter(indent=2)
1436 dict = self.Dictionary(key)
1438 dict = self.Dictionary()
1439 return pp.pformat(dict)
1441 def FindIxes(self, paths, prefix, suffix):
1443 Search a list of paths for something that matches the prefix and suffix.
1445 paths - the list of paths or nodes.
1446 prefix - construction variable for the prefix.
1447 suffix - construction variable for the suffix.
1450 suffix = self.subst('$'+suffix)
1451 prefix = self.subst('$'+prefix)
1454 dir,name = os.path.split(str(path))
1455 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
1458 def ParseConfig(self, command, function=None, unique=1):
1460 Use the specified function to parse the output of the command
1461 in order to modify the current environment. The 'command' can
1462 be a string or a list of strings representing a command and
1463 its arguments. 'Function' is an optional argument that takes
1464 the environment, the output of the command, and the unique flag.
1465 If no function is specified, MergeFlags, which treats the output
1466 as the result of a typical 'X-config' command (i.e. gtk-config),
1467 will merge the output into the appropriate variables.
1469 if function is None:
1470 def parse_conf(env, cmd, unique=unique):
1471 return env.MergeFlags(cmd, unique)
1472 function = parse_conf
1473 if SCons.Util.is_List(command):
1474 command = ' '.join(command)
1475 command = self.subst(command)
1476 return function(self, self.backtick(command))
1478 def ParseDepends(self, filename, must_exist=None, only_one=0):
1480 Parse a mkdep-style file for explicit dependencies. This is
1481 completely abusable, and should be unnecessary in the "normal"
1482 case of proper SCons configuration, but it may help make
1483 the transition from a Make hierarchy easier for some people
1484 to swallow. It can also be genuinely useful when using a tool
1485 that can write a .d file, but for which writing a scanner would
1488 filename = self.subst(filename)
1490 fp = open(filename, 'r')
1495 lines = SCons.Util.LogicalLines(fp).readlines()
1496 lines = [l for l in lines if l[0] != '#']
1500 target, depends = line.split(':', 1)
1501 except (AttributeError, TypeError, ValueError):
1502 # Python 1.5.2 throws TypeError if line isn't a string,
1503 # Python 2.x throws AttributeError because it tries
1504 # to call line.split(). Either can throw ValueError
1505 # if the line doesn't split into two or more elements.
1508 tdlist.append((target.split(), depends.split()))
1510 targets = reduce(lambda x, y: x+y, [p[0] for p in tdlist])
1511 if len(targets) > 1:
1512 raise SCons.Errors.UserError(
1513 "More than one dependency target found in `%s': %s"
1514 % (filename, targets))
1515 for target, depends in tdlist:
1516 self.Depends(target, depends)
1518 def Platform(self, platform):
1519 platform = self.subst(platform)
1520 return SCons.Platform.Platform(platform)(self)
1522 def Prepend(self, **kw):
1523 """Prepend values to existing construction variables
1526 kw = copy_non_reserved_keywords(kw)
1527 for key, val in kw.items():
1528 # It would be easier on the eyes to write this using
1529 # "continue" statements whenever we finish processing an item,
1530 # but Python 1.5.2 apparently doesn't let you use "continue"
1531 # within try:-except: blocks, so we have to nest our code.
1533 orig = self._dict[key]
1535 # No existing variable in the environment, so just set
1536 # it to the new value.
1537 self._dict[key] = val
1540 # Check if the original looks like a dictionary.
1541 # If it is, we can't just try adding the value because
1542 # dictionaries don't have __add__() methods, and
1543 # things like UserList will incorrectly coerce the
1544 # original dict to a list (which we don't want).
1545 update_dict = orig.update
1546 except AttributeError:
1548 # Most straightforward: just try to add them
1549 # together. This will work in most cases, when the
1550 # original and new values are of compatible types.
1551 self._dict[key] = val + orig
1552 except (KeyError, TypeError):
1554 # Check if the added value is a list.
1555 add_to_val = val.append
1556 except AttributeError:
1557 # The added value isn't a list, but the
1558 # original is (by process of elimination),
1559 # so insert the the new value in the original
1560 # (if there's one to insert).
1564 # The added value is a list, so append
1565 # the original to it (if there's a value
1569 self._dict[key] = val
1571 # The original looks like a dictionary, so update it
1572 # based on what we think the value looks like.
1573 if SCons.Util.is_List(val):
1579 except (AttributeError, TypeError, ValueError):
1580 if SCons.Util.is_Dict(val):
1581 for k, v in val.items():
1585 self.scanner_map_delete(kw)
1587 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep,
1589 """Prepend path elements to the path 'name' in the 'ENV'
1590 dictionary for this environment. Will only add any particular
1591 path once, and will normpath and normcase all paths to help
1592 assure this. This can also handle the case where the env
1593 variable is a list instead of a string.
1595 If delete_existing is 0, a newpath which is already in the path
1596 will not be moved to the front (it will be left where it is).
1600 if envname in self._dict and name in self._dict[envname]:
1601 orig = self._dict[envname][name]
1603 nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing,
1604 canonicalize=self._canonicalize)
1606 if envname not in self._dict:
1607 self._dict[envname] = {}
1609 self._dict[envname][name] = nv
1611 def PrependUnique(self, delete_existing=0, **kw):
1612 """Prepend values to existing construction variables
1613 in an Environment, if they're not already there.
1614 If delete_existing is 1, removes existing values first, so
1615 values move to front.
1617 kw = copy_non_reserved_keywords(kw)
1618 for key, val in kw.items():
1619 if SCons.Util.is_List(val):
1620 val = _delete_duplicates(val, not delete_existing)
1621 if key not in self._dict or self._dict[key] in ('', None):
1622 self._dict[key] = val
1623 elif SCons.Util.is_Dict(self._dict[key]) and \
1624 SCons.Util.is_Dict(val):
1625 self._dict[key].update(val)
1626 elif SCons.Util.is_List(val):
1627 dk = self._dict[key]
1628 if not SCons.Util.is_List(dk):
1631 dk = [x for x in dk if x not in val]
1633 val = [x for x in val if x not in dk]
1634 self._dict[key] = val + dk
1636 dk = self._dict[key]
1637 if SCons.Util.is_List(dk):
1638 # By elimination, val is not a list. Since dk is a
1639 # list, wrap val in a list first.
1641 dk = [x for x in dk if x not in val]
1642 self._dict[key] = [val] + dk
1645 self._dict[key] = [val] + dk
1648 dk = [x for x in dk if x not in val]
1649 self._dict[key] = val + dk
1650 self.scanner_map_delete(kw)
1652 def Replace(self, **kw):
1653 """Replace existing construction variables in an Environment
1654 with new construction variables and/or values.
1657 kwbd = kw['BUILDERS']
1661 kwbd = semi_deepcopy(kwbd)
1663 self.__setitem__('BUILDERS', kwbd)
1664 kw = copy_non_reserved_keywords(kw)
1665 self._update(semi_deepcopy(kw))
1666 self.scanner_map_delete(kw)
1668 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1670 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1672 env - Environment used to interpolate variables.
1673 path - the path that will be modified.
1674 old_prefix - construction variable for the old prefix.
1675 old_suffix - construction variable for the old suffix.
1676 new_prefix - construction variable for the new prefix.
1677 new_suffix - construction variable for the new suffix.
1679 old_prefix = self.subst('$'+old_prefix)
1680 old_suffix = self.subst('$'+old_suffix)
1682 new_prefix = self.subst('$'+new_prefix)
1683 new_suffix = self.subst('$'+new_suffix)
1685 dir,name = os.path.split(str(path))
1686 if name[:len(old_prefix)] == old_prefix:
1687 name = name[len(old_prefix):]
1688 if name[-len(old_suffix):] == old_suffix:
1689 name = name[:-len(old_suffix)]
1690 return os.path.join(dir, new_prefix+name+new_suffix)
1692 def SetDefault(self, **kw):
1698 def _find_toolpath_dir(self, tp):
1699 return self.fs.Dir(self.subst(tp)).srcnode().abspath
1701 def Tool(self, tool, toolpath=None, **kw):
1702 if SCons.Util.is_String(tool):
1703 tool = self.subst(tool)
1704 if toolpath is None:
1705 toolpath = self.get('toolpath', [])
1706 toolpath = list(map(self._find_toolpath_dir, toolpath))
1707 tool = SCons.Tool.Tool(tool, toolpath, **kw)
1710 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1711 """Find prog in the path.
1715 path = self['ENV']['PATH']
1718 elif SCons.Util.is_String(path):
1719 path = self.subst(path)
1722 pathext = self['ENV']['PATHEXT']
1725 elif SCons.Util.is_String(pathext):
1726 pathext = self.subst(pathext)
1727 prog = self.subst(prog)
1728 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1729 if path: return path
1732 #######################################################################
1733 # Public methods for doing real "SCons stuff" (manipulating
1734 # dependencies, setting attributes on targets, etc.). These begin
1735 # with upper-case letters. The essential characteristic of methods
1736 # in this section is that they all *should* have corresponding
1737 # same-named global functions.
1738 #######################################################################
1740 def Action(self, *args, **kw):
1741 def subst_string(a, self=self):
1742 if SCons.Util.is_String(a):
1745 nargs = list(map(subst_string, args))
1746 nkw = self.subst_kw(kw)
1747 return SCons.Action.Action(*nargs, **nkw)
1749 def AddPreAction(self, files, action):
1750 nodes = self.arg2nodes(files, self.fs.Entry)
1751 action = SCons.Action.Action(action)
1753 for executor in [n.get_executor() for n in nodes]:
1755 for executor in uniq.keys():
1756 executor.add_pre_action(action)
1759 def AddPostAction(self, files, action):
1760 nodes = self.arg2nodes(files, self.fs.Entry)
1761 action = SCons.Action.Action(action)
1763 for executor in [n.get_executor() for n in nodes]:
1765 for executor in uniq.keys():
1766 executor.add_post_action(action)
1769 def Alias(self, target, source=[], action=None, **kw):
1770 tlist = self.arg2nodes(target, self.ans.Alias)
1771 if not SCons.Util.is_List(source):
1773 source = [_f for _f in source if _f]
1777 # There are no source files and no action, so just
1778 # return a target list of classic Alias Nodes, without
1779 # any builder. The externally visible effect is that
1780 # this will make the wrapping Script.BuildTask class
1781 # say that there's "Nothing to be done" for this Alias,
1782 # instead of that it's "up to date."
1785 # No action, but there are sources. Re-call all the target
1786 # builders to add the sources to each target.
1789 bld = t.get_builder(AliasBuilder)
1790 result.extend(bld(self, t, source))
1793 nkw = self.subst_kw(kw)
1795 'action' : SCons.Action.Action(action),
1796 'source_factory' : self.fs.Entry,
1798 'is_explicit' : None,
1800 bld = SCons.Builder.Builder(**nkw)
1802 # Apply the Builder separately to each target so that the Aliases
1803 # stay separate. If we did one "normal" Builder call with the
1804 # whole target list, then all of the target Aliases would be
1805 # associated under a single Executor.
1808 # Calling the convert() method will cause a new Executor to be
1809 # created from scratch, so we have to explicitly initialize
1810 # it with the target's existing sources, plus our new ones,
1811 # so nothing gets lost.
1813 if b is None or b is AliasBuilder:
1816 nkw['action'] = b.action + action
1817 b = SCons.Builder.Builder(**nkw)
1819 result.extend(b(self, t, t.sources + source))
1822 def AlwaysBuild(self, *targets):
1825 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1827 t.set_always_build()
1830 def BuildDir(self, *args, **kw):
1831 if 'build_dir' in kw:
1832 kw['variant_dir'] = kw['build_dir']
1834 return self.VariantDir(*args, **kw)
1836 def Builder(self, **kw):
1837 nkw = self.subst_kw(kw)
1838 return SCons.Builder.Builder(**nkw)
1840 def CacheDir(self, path):
1841 import SCons.CacheDir
1842 if path is not None:
1843 path = self.subst(path)
1844 self._CacheDir_path = path
1846 def Clean(self, targets, files):
1848 tlist = self.arg2nodes(targets, self.fs.Entry)
1849 flist = self.arg2nodes(files, self.fs.Entry)
1852 CleanTargets[t].extend(flist)
1854 CleanTargets[t] = flist
1856 def Configure(self, *args, **kw):
1859 nargs = nargs + self.subst_list(args)[0]
1860 nkw = self.subst_kw(kw)
1861 nkw['_depth'] = kw.get('_depth', 0) + 1
1863 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1866 return SCons.SConf.SConf(*nargs, **nkw)
1868 def Command(self, target, source, action, **kw):
1869 """Builds the supplied target files from the supplied
1870 source files using the supplied action. Action may
1871 be any type that the Builder constructor will accept
1875 'target_factory' : self.fs.Entry,
1876 'source_factory' : self.fs.Entry,
1878 try: bkw['source_scanner'] = kw['source_scanner']
1879 except KeyError: pass
1880 else: del kw['source_scanner']
1881 bld = SCons.Builder.Builder(**bkw)
1882 return bld(self, target, source, **kw)
1884 def Depends(self, target, dependency):
1885 """Explicity specify that 'target's depend on 'dependency'."""
1886 tlist = self.arg2nodes(target, self.fs.Entry)
1887 dlist = self.arg2nodes(dependency, self.fs.Entry)
1889 t.add_dependency(dlist)
1892 def Dir(self, name, *args, **kw):
1895 s = self.subst(name)
1896 if SCons.Util.is_Sequence(s):
1899 result.append(self.fs.Dir(e, *args, **kw))
1901 return self.fs.Dir(s, *args, **kw)
1903 def NoClean(self, *targets):
1904 """Tags a target so that it will not be cleaned by -c"""
1907 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1912 def NoCache(self, *targets):
1913 """Tags a target so that it will not be cached"""
1916 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1921 def Entry(self, name, *args, **kw):
1924 s = self.subst(name)
1925 if SCons.Util.is_Sequence(s):
1928 result.append(self.fs.Entry(e, *args, **kw))
1930 return self.fs.Entry(s, *args, **kw)
1932 def Environment(self, **kw):
1933 return SCons.Environment.Environment(**self.subst_kw(kw))
1935 def Execute(self, action, *args, **kw):
1936 """Directly execute an action through an Environment
1938 action = self.Action(action, *args, **kw)
1939 result = action([], [], self)
1940 if isinstance(result, SCons.Errors.BuildError):
1941 errstr = result.errstr
1943 errstr = result.filename + ': ' + errstr
1944 sys.stderr.write("scons: *** %s\n" % errstr)
1945 return result.status
1949 def File(self, name, *args, **kw):
1952 s = self.subst(name)
1953 if SCons.Util.is_Sequence(s):
1956 result.append(self.fs.File(e, *args, **kw))
1958 return self.fs.File(s, *args, **kw)
1960 def FindFile(self, file, dirs):
1961 file = self.subst(file)
1962 nodes = self.arg2nodes(dirs, self.fs.Dir)
1963 return SCons.Node.FS.find_file(file, tuple(nodes))
1965 def Flatten(self, sequence):
1966 return SCons.Util.flatten(sequence)
1968 def GetBuildPath(self, files):
1969 result = list(map(str, self.arg2nodes(files, self.fs.Entry)))
1970 if SCons.Util.is_List(files):
1975 def Glob(self, pattern, ondisk=True, source=False, strings=False):
1976 return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
1978 def Ignore(self, target, dependency):
1979 """Ignore a dependency."""
1980 tlist = self.arg2nodes(target, self.fs.Entry)
1981 dlist = self.arg2nodes(dependency, self.fs.Entry)
1986 def Literal(self, string):
1987 return SCons.Subst.Literal(string)
1989 def Local(self, *targets):
1991 for targ in targets:
1992 if isinstance(targ, SCons.Node.Node):
1996 for t in self.arg2nodes(targ, self.fs.Entry):
2001 def Precious(self, *targets):
2004 tlist.extend(self.arg2nodes(t, self.fs.Entry))
2009 def Repository(self, *dirs, **kw):
2010 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
2011 self.fs.Repository(*dirs, **kw)
2013 def Requires(self, target, prerequisite):
2014 """Specify that 'prerequisite' must be built before 'target',
2015 (but 'target' does not actually depend on 'prerequisite'
2016 and need not be rebuilt if it changes)."""
2017 tlist = self.arg2nodes(target, self.fs.Entry)
2018 plist = self.arg2nodes(prerequisite, self.fs.Entry)
2020 t.add_prerequisite(plist)
2023 def Scanner(self, *args, **kw):
2026 if SCons.Util.is_String(arg):
2027 arg = self.subst(arg)
2029 nkw = self.subst_kw(kw)
2030 return SCons.Scanner.Base(*nargs, **nkw)
2032 def SConsignFile(self, name=".sconsign", dbm_module=None):
2033 if name is not None:
2034 name = self.subst(name)
2035 if not os.path.isabs(name):
2036 name = os.path.join(str(self.fs.SConstruct_dir), name)
2038 name = os.path.normpath(name)
2039 sconsign_dir = os.path.dirname(name)
2040 if sconsign_dir and not os.path.exists(sconsign_dir):
2041 self.Execute(SCons.Defaults.Mkdir(sconsign_dir))
2042 SCons.SConsign.File(name, dbm_module)
2044 def SideEffect(self, side_effect, target):
2045 """Tell scons that side_effects are built as side
2046 effects of building targets."""
2047 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
2048 targets = self.arg2nodes(target, self.fs.Entry)
2050 for side_effect in side_effects:
2051 if side_effect.multiple_side_effect_has_builder():
2052 raise SCons.Errors.UserError("Multiple ways to build the same target were specified for: %s" % str(side_effect))
2053 side_effect.add_source(targets)
2054 side_effect.side_effect = 1
2055 self.Precious(side_effect)
2056 for target in targets:
2057 target.side_effects.append(side_effect)
2060 def SourceCode(self, entry, builder):
2061 """Arrange for a source code builder for (part of) a tree."""
2062 entries = self.arg2nodes(entry, self.fs.Entry)
2063 for entry in entries:
2064 entry.set_src_builder(builder)
2067 def SourceSignatures(self, type):
2068 global _warn_source_signatures_deprecated
2069 if _warn_source_signatures_deprecated:
2070 msg = "The env.SourceSignatures() method is deprecated;\n" + \
2071 "\tconvert your build to use the env.Decider() method instead."
2072 SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
2073 _warn_source_signatures_deprecated = False
2074 type = self.subst(type)
2075 self.src_sig_type = type
2077 if not SCons.Util.md5:
2078 raise UserError("MD5 signatures are not available in this version of Python.")
2079 self.decide_source = self._changed_content
2080 elif type == 'timestamp':
2081 self.decide_source = self._changed_timestamp_match
2083 raise UserError("Unknown source signature type '%s'" % type)
2085 def Split(self, arg):
2086 """This function converts a string or list into a list of strings
2087 or Nodes. This makes things easier for users by allowing files to
2088 be specified as a white-space separated list to be split.
2089 The input rules are:
2090 - A single string containing names separated by spaces. These will be
2091 split apart at the spaces.
2092 - A single Node instance
2093 - A list containing either strings or Node instances. Any strings
2094 in the list are not split at spaces.
2095 In all cases, the function returns a list of Nodes and strings."""
2096 if SCons.Util.is_List(arg):
2097 return list(map(self.subst, arg))
2098 elif SCons.Util.is_String(arg):
2099 return self.subst(arg).split()
2101 return [self.subst(arg)]
2103 def TargetSignatures(self, type):
2104 global _warn_target_signatures_deprecated
2105 if _warn_target_signatures_deprecated:
2106 msg = "The env.TargetSignatures() method is deprecated;\n" + \
2107 "\tconvert your build to use the env.Decider() method instead."
2108 SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
2109 _warn_target_signatures_deprecated = False
2110 type = self.subst(type)
2111 self.tgt_sig_type = type
2112 if type in ('MD5', 'content'):
2113 if not SCons.Util.md5:
2114 raise UserError("MD5 signatures are not available in this version of Python.")
2115 self.decide_target = self._changed_content
2116 elif type == 'timestamp':
2117 self.decide_target = self._changed_timestamp_match
2118 elif type == 'build':
2119 self.decide_target = self._changed_build
2120 elif type == 'source':
2121 self.decide_target = self._changed_source
2123 raise UserError("Unknown target signature type '%s'"%type)
2125 def Value(self, value, built_value=None):
2128 return SCons.Node.Python.Value(value, built_value)
2130 def VariantDir(self, variant_dir, src_dir, duplicate=1):
2131 variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
2132 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
2133 self.fs.VariantDir(variant_dir, src_dir, duplicate)
2135 def FindSourceFiles(self, node='.'):
2136 """ returns a list of all source files.
2138 node = self.arg2nodes(node, self.fs.Entry)[0]
2141 # Uncomment this and get rid of the global definition when we
2142 # drop support for pre-2.2 Python versions.
2143 #def build_source(ss, result):
2145 # if isinstance(s, SCons.Node.FS.Dir):
2146 # build_source(s.all_children(), result)
2147 # elif s.has_builder():
2148 # build_source(s.sources, result)
2149 # elif isinstance(s.disambiguate(), SCons.Node.FS.File):
2151 build_source(node.all_children(), sources)
2153 # THIS CODE APPEARS TO HAVE NO EFFECT
2154 # # get the final srcnode for all nodes, this means stripping any
2155 # # attached build node by calling the srcnode function
2156 # for file in sources:
2157 # srcnode = file.srcnode()
2158 # while srcnode != file.srcnode():
2159 # srcnode = file.srcnode()
2162 return list(set(sources))
2164 def FindInstalledFiles(self):
2165 """ returns the list of all targets of the Install and InstallAs Builder.
2167 from SCons.Tool import install
2168 if install._UNIQUE_INSTALLED_FILES is None:
2169 install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
2170 return install._UNIQUE_INSTALLED_FILES
2172 class OverrideEnvironment(Base):
2173 """A proxy that overrides variables in a wrapped construction
2174 environment by returning values from an overrides dictionary in
2175 preference to values from the underlying subject environment.
2177 This is a lightweight (I hope) proxy that passes through most use of
2178 attributes to the underlying Environment.Base class, but has just
2179 enough additional methods defined to act like a real construction
2180 environment with overridden values. It can wrap either a Base
2181 construction environment, or another OverrideEnvironment, which
2182 can in turn nest arbitrary OverrideEnvironments...
2184 Note that we do *not* call the underlying base class
2185 (SubsitutionEnvironment) initialization, because we get most of those
2186 from proxying the attributes of the subject construction environment.
2187 But because we subclass SubstitutionEnvironment, this class also
2188 has inherited arg2nodes() and subst*() methods; those methods can't
2189 be proxied because they need *this* object's methods to fetch the
2190 values from the overrides dictionary.
2193 def __init__(self, subject, overrides={}):
2194 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
2195 self.__dict__['__subject'] = subject
2196 self.__dict__['overrides'] = overrides
2198 # Methods that make this class act like a proxy.
2199 def __getattr__(self, name):
2200 return getattr(self.__dict__['__subject'], name)
2201 def __setattr__(self, name, value):
2202 setattr(self.__dict__['__subject'], name, value)
2204 # Methods that make this class act like a dictionary.
2205 def __getitem__(self, key):
2207 return self.__dict__['overrides'][key]
2209 return self.__dict__['__subject'].__getitem__(key)
2210 def __setitem__(self, key, value):
2211 if not is_valid_construction_var(key):
2212 raise SCons.Errors.UserError("Illegal construction variable `%s'" % key)
2213 self.__dict__['overrides'][key] = value
2214 def __delitem__(self, key):
2216 del self.__dict__['overrides'][key]
2222 result = self.__dict__['__subject'].__delitem__(key)
2228 def get(self, key, default=None):
2229 """Emulates the get() method of dictionaries."""
2231 return self.__dict__['overrides'][key]
2233 return self.__dict__['__subject'].get(key, default)
2234 def has_key(self, key):
2236 self.__dict__['overrides'][key]
2239 return key in self.__dict__['__subject']
2240 def __contains__(self, key):
2241 if self.__dict__['overrides'].__contains__(key):
2243 return self.__dict__['__subject'].__contains__(key)
2244 def Dictionary(self):
2245 """Emulates the items() method of dictionaries."""
2246 d = self.__dict__['__subject'].Dictionary().copy()
2247 d.update(self.__dict__['overrides'])
2250 """Emulates the items() method of dictionaries."""
2251 return self.Dictionary().items()
2253 # Overridden private construction environment methods.
2254 def _update(self, dict):
2255 """Update an environment's values directly, bypassing the normal
2256 checks that occur when users try to set items.
2258 self.__dict__['overrides'].update(dict)
2261 return self.__dict__['__subject'].gvars()
2264 lvars = self.__dict__['__subject'].lvars()
2265 lvars.update(self.__dict__['overrides'])
2268 # Overridden public construction environment methods.
2269 def Replace(self, **kw):
2270 kw = copy_non_reserved_keywords(kw)
2271 self.__dict__['overrides'].update(semi_deepcopy(kw))
2273 # The entry point that will be used by the external world
2274 # to refer to a construction environment. This allows the wrapper
2275 # interface to extend a construction environment for its own purposes
2276 # by subclassing SCons.Environment.Base and then assigning the
2277 # class to SCons.Environment.Environment.
2281 # An entry point for returning a proxy subclass instance that overrides
2282 # the subst*() methods so they don't actually perform construction
2283 # variable substitution. This is specifically intended to be the shim
2284 # layer in between global function calls (which don't want construction
2285 # variable substitution) and the DefaultEnvironment() (which would
2286 # substitute variables if left to its own devices)."""
2288 # We have to wrap this in a function that allows us to delay definition of
2289 # the class until it's necessary, so that when it subclasses Environment
2290 # it will pick up whatever Environment subclass the wrapper interface
2291 # might have assigned to SCons.Environment.Environment.
2293 def NoSubstitutionProxy(subject):
2294 class _NoSubstitutionProxy(Environment):
2295 def __init__(self, subject):
2296 self.__dict__['__subject'] = subject
2297 def __getattr__(self, name):
2298 return getattr(self.__dict__['__subject'], name)
2299 def __setattr__(self, name, value):
2300 return setattr(self.__dict__['__subject'], name, value)
2301 def raw_to_mode(self, dict):
2309 def subst(self, string, *args, **kwargs):
2311 def subst_kw(self, kw, *args, **kwargs):
2313 def subst_list(self, string, *args, **kwargs):
2314 nargs = (string, self,) + args
2317 self.raw_to_mode(nkw)
2318 return SCons.Subst.scons_subst_list(*nargs, **nkw)
2319 def subst_target_source(self, string, *args, **kwargs):
2320 nargs = (string, self,) + args
2323 self.raw_to_mode(nkw)
2324 return SCons.Subst.scons_subst(*nargs, **nkw)
2325 return _NoSubstitutionProxy(subject)
2329 # indent-tabs-mode:nil
2331 # vim: set expandtab tabstop=4 shiftwidth=4: