3 Base class for construction Environments. These are
4 the primary objects used to communicate dependency and
5 construction information to the build engine.
7 Keyword arguments supplied when the construction Environment
8 is created are construction variables used to initialize the
15 # Permission is hereby granted, free of charge, to any person obtaining
16 # a copy of this software and associated documentation files (the
17 # "Software"), to deal in the Software without restriction, including
18 # without limitation the rights to use, copy, modify, merge, publish,
19 # distribute, sublicense, and/or sell copies of the Software, and to
20 # permit persons to whom the Software is furnished to do so, subject to
21 # the following conditions:
23 # The above copyright notice and this permission notice shall be included
24 # in all copies or substantial portions of the Software.
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
27 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
42 from UserDict import UserDict
46 from SCons.Debug import logInstanceCreation
50 import SCons.Node.Alias
52 import SCons.Node.Python
57 import SCons.Sig.TimeStamp
71 # Pull UserError into the global name space for the benefit of
72 # Environment().SourceSignatures(), which has some import statements
73 # which seem to mess up its ability to reference SCons directly.
74 UserError = SCons.Errors.UserError
76 def installFunc(target, source, env):
77 """Install a source file into a target using the function specified
78 as the INSTALL construction variable."""
80 install = env['INSTALL']
82 raise SCons.Errors.UserError('Missing INSTALL construction variable.')
83 return install(target[0].path, source[0].path, env)
85 def installString(target, source, env):
86 return env.subst(env['INSTALLSTR'], 0, target, source)
88 installAction = SCons.Action.Action(installFunc, installString)
90 InstallBuilder = SCons.Builder.Builder(action=installAction,
91 name='InstallBuilder')
93 def alias_builder(env, target, source):
96 AliasBuilder = SCons.Builder.Builder(action = alias_builder,
97 target_factory = SCons.Node.Alias.default_ans.Alias,
98 source_factory = SCons.Node.FS.Entry,
104 """deepcopy lists and dictionaries, and just copy the reference
105 for everything else."""
106 if SCons.Util.is_Dict(x):
109 copy[key] = our_deepcopy(x[key])
110 elif SCons.Util.is_List(x):
111 copy = map(our_deepcopy, x)
113 copy = x.__class__(copy)
114 except AttributeError:
120 def apply_tools(env, tools, toolpath):
121 # Store the toolpath in the Environment.
122 if toolpath is not None:
123 env['toolpath'] = toolpath
127 # Filter out null tools from the list.
128 for tool in filter(None, tools):
129 if SCons.Util.is_List(tool) or type(tool)==type(()):
131 toolargs = tool[1] # should be a dict of kw args
132 tool = apply(env.Tool, [toolname], toolargs)
136 # These names are controlled by SCons; users should never set or override
137 # them. This warning can optionally be turned off, but scons will still
138 # ignore the illegal variable names even if it's off.
139 reserved_construction_var_names = \
140 ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
142 def copy_non_reserved_keywords(dict):
143 result = our_deepcopy(dict)
144 for k in result.keys():
145 if k in reserved_construction_var_names:
146 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
147 "Ignoring attempt to set reserved variable `%s'" % k)
151 def _set_reserved(env, key, value):
152 msg = "Ignoring attempt to set reserved variable `%s'" % key
153 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg)
155 def _set_BUILDERS(env, key, value):
161 env._dict[key] = BuilderDict(kwbd, env)
162 env._dict[key].update(value)
164 def _set_SCANNERS(env, key, value):
165 env._dict[key] = value
166 env.scanner_map_delete()
168 class BuilderWrapper:
169 """Wrapper class that associates an environment with a Builder at
171 def __init__(self, env, builder):
173 self.builder = builder
175 def __call__(self, target=None, source=_null, *args, **kw):
179 if not target is None and not SCons.Util.is_List(target):
181 if not source is None and not SCons.Util.is_List(source):
183 return apply(self.builder, (self.env, target, source) + args, kw)
185 # This allows a Builder to be executed directly
186 # through the Environment to which it's attached.
187 # In practice, we shouldn't need this, because
188 # builders actually get executed through a Node.
189 # But we do have a unit test for this, and can't
190 # yet rule out that it would be useful in the
191 # future, so leave it for now.
192 def execute(self, **kw):
194 apply(self.builder.execute, (), kw)
196 class BuilderDict(UserDict):
197 """This is a dictionary-like class used by an Environment to hold
198 the Builders. We need to do this because every time someone changes
199 the Builders in the Environment's BUILDERS dictionary, we must
200 update the Environment's attributes."""
201 def __init__(self, dict, env):
202 # Set self.env before calling the superclass initialization,
203 # because it will end up calling our other methods, which will
204 # need to point the values in this dictionary to self.env.
206 UserDict.__init__(self, dict)
208 def __setitem__(self, item, val):
209 UserDict.__setitem__(self, item, val)
211 self.setenvattr(item, val)
212 except AttributeError:
213 # Have to catch this because sometimes __setitem__ gets
214 # called out of __init__, when we don't have an env
215 # attribute yet, nor do we want one!
218 def setenvattr(self, item, val):
219 """Set the corresponding environment attribute for this Builder.
221 If the value is already a BuilderWrapper, we pull the builder
222 out of it and make another one, so that making a copy of an
223 existing BuilderDict is guaranteed separate wrappers for each
224 Builder + Environment pair."""
226 builder = val.builder
227 except AttributeError:
229 setattr(self.env, item, BuilderWrapper(self.env, builder))
231 def __delitem__(self, item):
232 UserDict.__delitem__(self, item)
233 delattr(self.env, item)
235 def update(self, dict):
236 for i, v in dict.items():
237 self.__setitem__(i, v)
239 class SubstitutionEnvironment:
240 """Base class for different flavors of construction environments.
242 This class contains a minimal set of methods that handle contruction
243 variable expansion and conversion of strings to Nodes, which may or
244 may not be actually useful as a stand-alone class. Which methods
245 ended up in this class is pretty arbitrary right now. They're
246 basically the ones which we've empirically determined are common to
247 the different construction environment subclasses, and most of the
248 others that use or touch the underlying dictionary of construction
251 Eventually, this class should contain all the methods that we
252 determine are necessary for a "minimal" interface to the build engine.
253 A full "native Python" SCons environment has gotten pretty heavyweight
254 with all of the methods and Tools and construction variables we've
255 jammed in there, so it would be nice to have a lighter weight
256 alternative for interfaces that don't need all of the bells and
257 whistles. (At some point, we'll also probably rename this class
258 "Base," since that more reflects what we want this class to become,
259 but because we've released comments that tell people to subclass
260 Environment.Base to create their own flavors of construction
261 environment, we'll save that for a future refactoring when this
262 class actually becomes useful.)
265 if SCons.Memoize.use_memoizer:
266 __metaclass__ = SCons.Memoize.Memoized_Metaclass
268 def __init__(self, **kw):
269 """Initialization of an underlying SubstitutionEnvironment class.
271 if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
272 self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS()
273 self.ans = SCons.Node.Alias.default_ans
274 self.lookup_list = SCons.Node.arg2nodes_lookups
275 self._dict = kw.copy()
278 def _init_special(self):
279 """Initial the dispatch table for special handling of
280 special construction variables."""
282 for key in reserved_construction_var_names:
283 self._special[key] = _set_reserved
284 self._special['BUILDERS'] = _set_BUILDERS
285 self._special['SCANNERS'] = _set_SCANNERS
287 def __cmp__(self, other):
288 return cmp(self._dict, other._dict)
290 def __delitem__(self, key):
294 def __getitem__(self, key):
295 return self._dict[key]
297 def __setitem__(self, key, value):
299 special = self._special.get(key)
301 special(self, key, value)
303 if not SCons.Util.is_valid_construction_var(key):
304 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
305 self._dict[key] = value
307 def get(self, key, default=None):
308 "Emulates the get() method of dictionaries."""
309 return self._dict.get(key, default)
311 def has_key(self, key):
312 return self._dict.has_key(key)
315 return self._dict.items()
317 def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
318 if node_factory is _null:
319 node_factory = self.fs.File
320 if lookup_list is _null:
321 lookup_list = self.lookup_list
326 if SCons.Util.is_List(args):
327 args = SCons.Util.flatten(args)
333 if SCons.Util.is_String(v):
335 for l in lookup_list:
340 if SCons.Util.is_String(n):
341 n = self.subst(n, raw=1)
344 if SCons.Util.is_List(n):
349 v = node_factory(self.subst(v, raw=1))
350 if SCons.Util.is_List(v):
365 def subst(self, string, raw=0, target=None, source=None, conv=None):
366 """Recursively interpolates construction variables from the
367 Environment into the specified string, returning the expanded
368 result. Construction variables are specified by a $ prefix
369 in the string and begin with an initial underscore or
370 alphabetic character followed by any number of underscores
371 or alphanumeric characters. The construction variable names
372 may be surrounded by curly braces to separate the name from
377 lvars['__env__'] = self
378 return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
380 def subst_kw(self, kw, raw=0, target=None, source=None):
382 for k, v in kw.items():
383 k = self.subst(k, raw, target, source)
384 if SCons.Util.is_String(v):
385 v = self.subst(v, raw, target, source)
389 def subst_list(self, string, raw=0, target=None, source=None, conv=None):
390 """Calls through to SCons.Subst.scons_subst_list(). See
391 the documentation for that function."""
394 lvars['__env__'] = self
395 return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
397 def subst_path(self, path, target=None, source=None):
398 """Substitute a path list, turning EntryProxies into Nodes
399 and leaving Nodes (and other objects) as-is."""
401 if not SCons.Util.is_List(path):
405 """This is the "string conversion" routine that we have our
406 substitutions use to return Nodes, not strings. This relies
407 on the fact that an EntryProxy object has a get() method that
408 returns the underlying Node that it wraps, which is a bit of
409 architectural dependence that we might need to break or modify
410 in the future in response to additional requirements."""
413 except AttributeError:
421 if SCons.Util.is_String(p):
422 p = self.subst(p, target=target, source=source, conv=s)
423 if SCons.Util.is_List(p):
427 # We have an object plus a string, or multiple
428 # objects that we need to smush together. No choice
429 # but to make them into a string.
430 p = string.join(map(SCons.Util.to_String, p), '')
436 subst_target_source = subst
438 def Override(self, overrides):
440 Produce a modified environment whose variables are overriden by
441 the overrides dictionaries. "overrides" is a dictionary that
442 will override the variables of this environment.
444 This function is much more efficient than Copy() or creating
445 a new Environment because it doesn't copy the construction
446 environment dictionary, it just wraps the underlying construction
447 environment, and doesn't even create a wrapper object if there
451 o = copy_non_reserved_keywords(overrides)
453 for key, value in o.items():
454 overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
456 env = OverrideEnvironment(self, overrides)
461 class Base(SubstitutionEnvironment):
462 """Base class for "real" construction Environments. These are the
463 primary objects used to communicate dependency and construction
464 information to the build engine.
466 Keyword arguments supplied when the construction Environment
467 is created are construction variables used to initialize the
471 if SCons.Memoize.use_memoizer:
472 __metaclass__ = SCons.Memoize.Memoized_Metaclass
474 #######################################################################
475 # This is THE class for interacting with the SCons build engine,
476 # and it contains a lot of stuff, so we're going to try to keep this
477 # a little organized by grouping the methods.
478 #######################################################################
480 #######################################################################
481 # Methods that make an Environment act like a dictionary. These have
482 # the expected standard names for Python mapping objects. Note that
483 # we don't actually make an Environment a subclass of UserDict for
484 # performance reasons. Note also that we only supply methods for
485 # dictionary functionality that we actually need and use.
486 #######################################################################
495 Initialization of a basic SCons construction environment,
496 including setting up special construction variables like BUILDER,
497 PLATFORM, etc., and searching for and applying available Tools.
499 Note that we do *not* call the underlying base class
500 (SubsitutionEnvironment) initialization, because we need to
501 initialize things in a very specific order that doesn't work
502 with the much simpler base class initialization.
504 if __debug__: logInstanceCreation(self, 'Environment.Base')
505 self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS()
506 self.ans = SCons.Node.Alias.default_ans
507 self.lookup_list = SCons.Node.arg2nodes_lookups
508 self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
511 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
514 platform = self._dict.get('PLATFORM', None)
516 platform = SCons.Platform.Platform()
517 if SCons.Util.is_String(platform):
518 platform = SCons.Platform.Platform(platform)
519 self._dict['PLATFORM'] = str(platform)
522 # Apply the passed-in variables and customizable options to the
523 # environment before calling the tools, because they may use
524 # some of them during initialization.
525 apply(self.Replace, (), kw)
530 tools = self._dict.get('TOOLS', None)
533 apply_tools(self, tools, toolpath)
535 # Now re-apply the passed-in variables and customizable options
536 # to the environment, since the values the user set explicitly
537 # should override any values set by the tools.
538 apply(self.Replace, (), kw)
542 #######################################################################
543 # Utility methods that are primarily for internal use by SCons.
544 # These begin with lower-case letters.
545 #######################################################################
547 def get_builder(self, name):
548 """Fetch the builder with the specified name from the environment.
551 return self._dict['BUILDERS'][name]
555 def get_calculator(self):
558 module = self._calc_module
559 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
560 except AttributeError:
561 # Note that we're calling get_calculator() here, so the
562 # DefaultEnvironment() must have a _calc_module attribute
563 # to avoid infinite recursion.
564 c = SCons.Defaults.DefaultEnvironment().get_calculator()
567 def get_factory(self, factory, default='File'):
568 """Return a factory function for creating Nodes for this
569 construction environment.
574 is_node = issubclass(factory, SCons.Node.Node)
576 # The specified factory isn't a Node itself--it's
577 # most likely None, or possibly a callable.
581 # The specified factory is a Node (sub)class. Try to
582 # return the FS method that corresponds to the Node's
583 # name--that is, we return self.fs.Dir if they want a Dir,
584 # self.fs.File for a File, etc.
585 try: name = factory.__name__
586 except AttributeError: pass
589 # They passed us None, or we picked up a name from a specified
590 # class, so return the FS method. (Note that we *don't*
591 # use our own self.{Dir,File} methods because that would
592 # cause env.subst() to be called twice on the file name,
593 # interfering with files that have $$ in them.)
594 factory = getattr(self.fs, name)
600 scanners = self._dict['SCANNERS']
605 # Reverse the scanner list so that, if multiple scanners
606 # claim they can scan the same suffix, earlier scanners
607 # in the list will overwrite later scanners, so that
608 # the result looks like a "first match" to the user.
609 if not SCons.Util.is_List(scanners):
610 scanners = [scanners]
612 scanners = scanners[:] # copy so reverse() doesn't mod original
614 for scanner in scanners:
615 for k in scanner.get_skeys(self):
619 def get_scanner(self, skey):
620 """Find the appropriate scanner given a key (usually a file suffix).
624 except (KeyError, TypeError): return None
630 def scanner_map_delete(self, kw=None):
631 """Delete the cached scanner map (if we need to).
633 if not kw is None and not kw.has_key('SCANNERS'):
637 def _update(self, dict):
638 """Update an environment's values directly, bypassing the normal
639 checks that occur when users try to set items.
642 self._dict.update(dict)
644 def use_build_signature(self):
646 return self._build_signature
647 except AttributeError:
648 b = SCons.Defaults.DefaultEnvironment()._build_signature
649 self._build_signature = b
652 #######################################################################
653 # Public methods for manipulating an Environment. These begin with
654 # upper-case letters. The essential characteristic of methods in
655 # this section is that they do *not* have corresponding same-named
656 # global functions. For example, a stand-alone Append() function
657 # makes no sense, because Append() is all about appending values to
658 # an Environment's construction variables.
659 #######################################################################
661 def Append(self, **kw):
662 """Append values to existing construction variables
665 kw = copy_non_reserved_keywords(kw)
666 for key, val in kw.items():
667 # It would be easier on the eyes to write this using
668 # "continue" statements whenever we finish processing an item,
669 # but Python 1.5.2 apparently doesn't let you use "continue"
670 # within try:-except: blocks, so we have to nest our code.
672 orig = self._dict[key]
674 # No existing variable in the environment, so just set
675 # it to the new value.
676 self._dict[key] = val
679 # Most straightforward: just try to add them
680 # together. This will work in most cases, when the
681 # original and new values are of compatible types.
682 self._dict[key] = orig + val
685 # Try to update a dictionary value with another.
686 # If orig isn't a dictionary, it won't have an
687 # update() method; if val isn't a dictionary,
688 # it won't have a keys() method. Either way,
689 # it's an AttributeError.
691 except AttributeError:
693 # Check if the original is a list.
694 add_to_orig = orig.append
695 except AttributeError:
696 # The original isn't a list, but the new
697 # value is (by process of elimination),
698 # so insert the original in the new value
699 # (if there's one to insert) and replace
700 # the variable with it.
703 self._dict[key] = val
705 # The original is a list, so append the new
706 # value to it (if there's a value to append).
709 self.scanner_map_delete(kw)
711 def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
712 """Append path elements to the path 'name' in the 'ENV'
713 dictionary for this environment. Will only add any particular
714 path once, and will normpath and normcase all paths to help
715 assure this. This can also handle the case where the env
716 variable is a list instead of a string.
720 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
721 orig = self._dict[envname][name]
723 nv = SCons.Util.AppendPath(orig, newpath, sep)
725 if not self._dict.has_key(envname):
726 self._dict[envname] = {}
728 self._dict[envname][name] = nv
730 def AppendUnique(self, **kw):
731 """Append values to existing construction variables
732 in an Environment, if they're not already there.
734 kw = copy_non_reserved_keywords(kw)
735 for key, val in kw.items():
736 if not self._dict.has_key(key) or not self._dict[key]:
737 self._dict[key] = val
738 elif SCons.Util.is_Dict(self._dict[key]) and \
739 SCons.Util.is_Dict(val):
740 self._dict[key].update(val)
741 elif SCons.Util.is_List(val):
743 if not SCons.Util.is_List(dk):
745 val = filter(lambda x, dk=dk: x not in dk, val)
746 self._dict[key] = dk + val
749 if SCons.Util.is_List(dk):
750 # By elimination, val is not a list. Since dk is a
751 # list, wrap val in a list first.
753 self._dict[key] = dk + [val]
755 self._dict[key] = self._dict[key] + val
756 self.scanner_map_delete(kw)
758 def Copy(self, tools=[], toolpath=None, **kw):
759 """Return a copy of a construction Environment. The
760 copy is like a Python "deep copy"--that is, independent
761 copies are made recursively of each objects--except that
762 a reference is copied when an object is not deep-copyable
763 (like a function). There are no references to any mutable
764 objects in the original Environment.
766 clone = copy.copy(self)
767 clone._dict = our_deepcopy(self._dict)
769 cbd = clone._dict['BUILDERS']
770 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
774 apply_tools(clone, tools, toolpath)
776 # Apply passed-in variables after the new tools.
777 kw = copy_non_reserved_keywords(kw)
779 for key, value in kw.items():
780 new[key] = SCons.Subst.scons_subst_once(value, self, key)
781 apply(clone.Replace, (), new)
782 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentCopy')
785 def Detect(self, progs):
786 """Return the first available program in progs. __cacheable__
788 if not SCons.Util.is_List(progs):
791 path = self.WhereIs(prog)
795 def Dictionary(self, *args):
798 dlist = map(lambda x, s=self: s._dict[x], args)
803 def Dump(self, key = None):
805 Using the standard Python pretty printer, dump the contents of the
806 scons build environment to stdout.
808 If the key passed in is anything other than None, then that will
809 be used as an index into the build environment dictionary and
810 whatever is found there will be fed into the pretty printer. Note
811 that this key is case sensitive.
814 pp = pprint.PrettyPrinter(indent=2)
816 dict = self.Dictionary(key)
818 dict = self.Dictionary()
819 return pp.pformat(dict)
821 def FindIxes(self, paths, prefix, suffix):
823 Search a list of paths for something that matches the prefix and suffix.
825 paths - the list of paths or nodes.
826 prefix - construction variable for the prefix.
827 suffix - construction variable for the suffix.
830 suffix = self.subst('$'+suffix)
831 prefix = self.subst('$'+prefix)
834 dir,name = os.path.split(str(path))
835 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
838 def ParseConfig(self, command, function=None, unique=1):
840 Use the specified function to parse the output of the command
841 in order to modify the current environment. The 'command' can
842 be a string or a list of strings representing a command and
843 it's arguments. 'Function' is an optional argument that takes
844 the environment and the output of the command. If no function is
845 specified, the output will be treated as the output of a typical
846 'X-config' command (i.e. gtk-config) and used to append to the
847 ASFLAGS, CCFLAGS, CPPFLAGS, CPPPATH, LIBPATH, LIBS, LINKFLAGS
848 and CCFLAGS variables.
851 # the default parse function
852 def parse_conf(env, output, fs=self.fs, unique=unique):
863 params = string.split(output)
864 append_next_arg_to='' # for multi-word args
866 if append_next_arg_to:
867 dict[append_next_arg_to].append(arg)
868 append_next_arg_to = ''
870 dict['LIBS'].append(fs.File(arg))
871 elif arg[:2] == '-L':
873 dict['LIBPATH'].append(arg[2:])
875 append_next_arg_to = 'LIBPATH'
876 elif arg[:2] == '-l':
878 dict['LIBS'].append(arg[2:])
880 append_next_arg_to = 'LIBS'
881 elif arg[:2] == '-I':
883 dict['CPPPATH'].append(arg[2:])
885 append_next_arg_to = 'CPPPATH'
886 elif arg[:4] == '-Wa,':
887 dict['ASFLAGS'].append(arg)
888 elif arg[:4] == '-Wl,':
889 dict['LINKFLAGS'].append(arg)
890 elif arg[:4] == '-Wp,':
891 dict['CPPFLAGS'].append(arg)
892 elif arg == '-framework':
893 dict['LINKFLAGS'].append(arg)
894 append_next_arg_to='LINKFLAGS'
895 elif arg == '-mno-cygwin':
896 dict['CCFLAGS'].append(arg)
897 dict['LINKFLAGS'].append(arg)
898 elif arg == '-mwindows':
899 dict['LINKFLAGS'].append(arg)
900 elif arg == '-pthread':
901 dict['CCFLAGS'].append(arg)
902 dict['LINKFLAGS'].append(arg)
904 dict['CCFLAGS'].append(arg)
906 appender = env.AppendUnique
908 appender = env.Append
909 apply(appender, (), dict)
912 function = parse_conf
913 if type(command) is type([]):
914 command = string.join(command)
915 command = self.subst(command)
916 return function(self, os.popen(command).read())
918 def ParseDepends(self, filename, must_exist=None, only_one=0):
920 Parse a mkdep-style file for explicit dependencies. This is
921 completely abusable, and should be unnecessary in the "normal"
922 case of proper SCons configuration, but it may help make
923 the transition from a Make hierarchy easier for some people
924 to swallow. It can also be genuinely useful when using a tool
925 that can write a .d file, but for which writing a scanner would
928 filename = self.subst(filename)
930 fp = open(filename, 'r')
935 lines = SCons.Util.LogicalLines(fp).readlines()
936 lines = filter(lambda l: l[0] != '#', lines)
940 target, depends = string.split(line, ':', 1)
941 except (AttributeError, TypeError, ValueError):
942 # Python 1.5.2 throws TypeError if line isn't a string,
943 # Python 2.x throws AttributeError because it tries
944 # to call line.split(). Either can throw ValueError
945 # if the line doesn't split into two or more elements.
948 tdlist.append((string.split(target), string.split(depends)))
950 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
952 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
953 for target, depends in tdlist:
954 self.Depends(target, depends)
956 def Platform(self, platform):
957 platform = self.subst(platform)
958 return SCons.Platform.Platform(platform)(self)
960 def Prepend(self, **kw):
961 """Prepend values to existing construction variables
964 kw = copy_non_reserved_keywords(kw)
965 for key, val in kw.items():
966 # It would be easier on the eyes to write this using
967 # "continue" statements whenever we finish processing an item,
968 # but Python 1.5.2 apparently doesn't let you use "continue"
969 # within try:-except: blocks, so we have to nest our code.
971 orig = self._dict[key]
973 # No existing variable in the environment, so just set
974 # it to the new value.
975 self._dict[key] = val
978 # Most straightforward: just try to add them
979 # together. This will work in most cases, when the
980 # original and new values are of compatible types.
981 self._dict[key] = val + orig
984 # Try to update a dictionary value with another.
985 # If orig isn't a dictionary, it won't have an
986 # update() method; if val isn't a dictionary,
987 # it won't have a keys() method. Either way,
988 # it's an AttributeError.
990 except AttributeError:
992 # Check if the added value is a list.
993 add_to_val = val.append
994 except AttributeError:
995 # The added value isn't a list, but the
996 # original is (by process of elimination),
997 # so insert the the new value in the original
998 # (if there's one to insert).
1002 # The added value is a list, so append
1003 # the original to it (if there's a value
1007 self._dict[key] = val
1008 self.scanner_map_delete(kw)
1010 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
1011 """Prepend path elements to the path 'name' in the 'ENV'
1012 dictionary for this environment. Will only add any particular
1013 path once, and will normpath and normcase all paths to help
1014 assure this. This can also handle the case where the env
1015 variable is a list instead of a string.
1019 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1020 orig = self._dict[envname][name]
1022 nv = SCons.Util.PrependPath(orig, newpath, sep)
1024 if not self._dict.has_key(envname):
1025 self._dict[envname] = {}
1027 self._dict[envname][name] = nv
1029 def PrependUnique(self, **kw):
1030 """Append values to existing construction variables
1031 in an Environment, if they're not already there.
1033 kw = copy_non_reserved_keywords(kw)
1034 for key, val in kw.items():
1035 if not self._dict.has_key(key) or not self._dict[key]:
1036 self._dict[key] = val
1037 elif SCons.Util.is_Dict(self._dict[key]) and \
1038 SCons.Util.is_Dict(val):
1039 self._dict[key].update(val)
1040 elif SCons.Util.is_List(val):
1041 dk = self._dict[key]
1042 if not SCons.Util.is_List(dk):
1044 val = filter(lambda x, dk=dk: x not in dk, val)
1045 self._dict[key] = val + dk
1047 dk = self._dict[key]
1048 if SCons.Util.is_List(dk):
1049 # By elimination, val is not a list. Since dk is a
1050 # list, wrap val in a list first.
1052 self._dict[key] = [val] + dk
1054 self._dict[key] = val + dk
1055 self.scanner_map_delete(kw)
1057 def Replace(self, **kw):
1058 """Replace existing construction variables in an Environment
1059 with new construction variables and/or values.
1062 kwbd = our_deepcopy(kw['BUILDERS'])
1064 self.__setitem__('BUILDERS', kwbd)
1067 kw = copy_non_reserved_keywords(kw)
1068 self._update(our_deepcopy(kw))
1069 self.scanner_map_delete(kw)
1071 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1073 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1075 env - Environment used to interpolate variables.
1076 path - the path that will be modified.
1077 old_prefix - construction variable for the old prefix.
1078 old_suffix - construction variable for the old suffix.
1079 new_prefix - construction variable for the new prefix.
1080 new_suffix - construction variable for the new suffix.
1082 old_prefix = self.subst('$'+old_prefix)
1083 old_suffix = self.subst('$'+old_suffix)
1085 new_prefix = self.subst('$'+new_prefix)
1086 new_suffix = self.subst('$'+new_suffix)
1088 dir,name = os.path.split(str(path))
1089 if name[:len(old_prefix)] == old_prefix:
1090 name = name[len(old_prefix):]
1091 if name[-len(old_suffix):] == old_suffix:
1092 name = name[:-len(old_suffix)]
1093 return os.path.join(dir, new_prefix+name+new_suffix)
1095 def SetDefault(self, **kw):
1097 if self._dict.has_key(k):
1099 apply(self.Replace, (), kw)
1101 def Tool(self, tool, toolpath=None, **kw):
1102 if SCons.Util.is_String(tool):
1103 tool = self.subst(tool)
1104 if toolpath is None:
1105 toolpath = self.get('toolpath', [])
1106 toolpath = map(self.subst, toolpath)
1107 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
1110 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1111 """Find prog in the path. __cacheable__
1115 path = self['ENV']['PATH']
1118 elif SCons.Util.is_String(path):
1119 path = self.subst(path)
1122 pathext = self['ENV']['PATHEXT']
1125 elif SCons.Util.is_String(pathext):
1126 pathext = self.subst(pathext)
1127 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1128 if path: return path
1131 #######################################################################
1132 # Public methods for doing real "SCons stuff" (manipulating
1133 # dependencies, setting attributes on targets, etc.). These begin
1134 # with upper-case letters. The essential characteristic of methods
1135 # in this section is that they all *should* have corresponding
1136 # same-named global functions.
1137 #######################################################################
1139 def Action(self, *args, **kw):
1140 nargs = self.subst(args)
1141 nkw = self.subst_kw(kw)
1142 return apply(SCons.Action.Action, nargs, nkw)
1144 def AddPreAction(self, files, action):
1145 nodes = self.arg2nodes(files, self.fs.Entry)
1146 action = SCons.Action.Action(action)
1148 for executor in map(lambda n: n.get_executor(), nodes):
1150 for executor in uniq.keys():
1151 executor.add_pre_action(action)
1154 def AddPostAction(self, files, action):
1155 nodes = self.arg2nodes(files, self.fs.Entry)
1156 action = SCons.Action.Action(action)
1158 for executor in map(lambda n: n.get_executor(), nodes):
1160 for executor in uniq.keys():
1161 executor.add_post_action(action)
1164 def Alias(self, target, source=[], action=None, **kw):
1165 tlist = self.arg2nodes(target, self.ans.Alias)
1166 if not SCons.Util.is_List(source):
1168 source = filter(None, source)
1172 # There are no source files and no action, so just
1173 # return a target list of classic Alias Nodes, without
1174 # any builder. The externally visible effect is that
1175 # this will make the wrapping Script.BuildTask class
1176 # say that there's "Nothing to be done" for this Alias,
1177 # instead of that it's "up to date."
1180 # No action, but there are sources. Re-call all the target
1181 # builders to add the sources to each target.
1184 bld = t.get_builder(AliasBuilder)
1185 result.extend(bld(self, t, source))
1188 nkw = self.subst_kw(kw)
1190 'action' : SCons.Action.Action(action),
1191 'source_factory' : self.fs.Entry,
1193 'is_explicit' : None,
1195 bld = apply(SCons.Builder.Builder, (), nkw)
1197 # Apply the Builder separately to each target so that the Aliases
1198 # stay separate. If we did one "normal" Builder call with the
1199 # whole target list, then all of the target Aliases would be
1200 # associated under a single Executor.
1203 # Calling the convert() method will cause a new Executor to be
1204 # created from scratch, so we have to explicitly initialize
1205 # it with the target's existing sources, plus our new ones,
1206 # so nothing gets lost.
1208 if b is None or b is AliasBuilder:
1211 nkw['action'] = b.action + action
1212 b = apply(SCons.Builder.Builder, (), nkw)
1214 result.extend(b(self, t, t.sources + source))
1217 def AlwaysBuild(self, *targets):
1220 tlist.extend(self.arg2nodes(t, self.fs.File))
1222 t.set_always_build()
1225 def BuildDir(self, build_dir, src_dir, duplicate=1):
1226 build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
1227 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1228 self.fs.BuildDir(build_dir, src_dir, duplicate)
1230 def Builder(self, **kw):
1231 nkw = self.subst_kw(kw)
1232 return apply(SCons.Builder.Builder, [], nkw)
1234 def CacheDir(self, path):
1235 self.fs.CacheDir(self.subst(path))
1237 def Clean(self, targets, files):
1239 tlist = self.arg2nodes(targets, self.fs.Entry)
1240 flist = self.arg2nodes(files, self.fs.Entry)
1243 CleanTargets[t].extend(flist)
1245 CleanTargets[t] = flist
1247 def Configure(self, *args, **kw):
1250 nargs = nargs + self.subst_list(args)[0]
1251 nkw = self.subst_kw(kw)
1252 nkw['_depth'] = kw.get('_depth', 0) + 1
1254 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1257 return apply(SCons.SConf.SConf, nargs, nkw)
1259 def Command(self, target, source, action, **kw):
1260 """Builds the supplied target files from the supplied
1261 source files using the supplied action. Action may
1262 be any type that the Builder constructor will accept
1266 'target_factory' : self.fs.Entry,
1267 'source_factory' : self.fs.Entry,
1269 try: bkw['source_scanner'] = kw['source_scanner']
1270 except KeyError: pass
1271 else: del kw['source_scanner']
1272 bld = apply(SCons.Builder.Builder, (), bkw)
1273 return apply(bld, (self, target, source), kw)
1275 def Depends(self, target, dependency):
1276 """Explicity specify that 'target's depend on 'dependency'."""
1277 tlist = self.arg2nodes(target, self.fs.Entry)
1278 dlist = self.arg2nodes(dependency, self.fs.Entry)
1280 t.add_dependency(dlist)
1283 def Dir(self, name, *args, **kw):
1286 return apply(self.fs.Dir, (self.subst(name),) + args, kw)
1288 def NoClean(self, *targets):
1289 """Tags a target so that it will not be cleaned by -c"""
1292 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1297 def Entry(self, name, *args, **kw):
1300 return apply(self.fs.Entry, (self.subst(name),) + args, kw)
1302 def Environment(self, **kw):
1303 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1305 def Execute(self, action, *args, **kw):
1306 """Directly execute an action through an Environment
1308 action = apply(self.Action, (action,) + args, kw)
1309 return action([], [], self)
1311 def File(self, name, *args, **kw):
1314 return apply(self.fs.File, (self.subst(name),) + args, kw)
1316 def FindFile(self, file, dirs):
1317 file = self.subst(file)
1318 nodes = self.arg2nodes(dirs, self.fs.Dir)
1319 return SCons.Node.FS.find_file(file, tuple(nodes))
1321 def Flatten(self, sequence):
1322 return SCons.Util.flatten(sequence)
1324 def GetBuildPath(self, files):
1325 result = map(str, self.arg2nodes(files, self.fs.Entry))
1326 if SCons.Util.is_List(files):
1331 def Ignore(self, target, dependency):
1332 """Ignore a dependency."""
1333 tlist = self.arg2nodes(target, self.fs.Entry)
1334 dlist = self.arg2nodes(dependency, self.fs.Entry)
1339 def Install(self, dir, source):
1340 """Install specified files in the given directory."""
1342 dnodes = self.arg2nodes(dir, self.fs.Dir)
1344 raise SCons.Errors.UserError, "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)
1346 sources = self.arg2nodes(source, self.fs.File)
1348 if SCons.Util.is_List(source):
1349 raise SCons.Errors.UserError, "Source `%s' of Install() contains one or more non-files. Install() source must be one or more files." % repr(map(str, source))
1351 raise SCons.Errors.UserError, "Source `%s' of Install() is not a file. Install() source must be one or more files." % str(source)
1353 for dnode in dnodes:
1355 target = self.fs.File(src.name, dnode)
1356 tgt.extend(InstallBuilder(self, target, src))
1359 def InstallAs(self, target, source):
1360 """Install sources as targets."""
1361 sources = self.arg2nodes(source, self.fs.File)
1362 targets = self.arg2nodes(target, self.fs.File)
1364 for src, tgt in map(lambda x, y: (x, y), sources, targets):
1365 result.extend(InstallBuilder(self, tgt, src))
1368 def Literal(self, string):
1369 return SCons.Subst.Literal(string)
1371 def Local(self, *targets):
1373 for targ in targets:
1374 if isinstance(targ, SCons.Node.Node):
1378 for t in self.arg2nodes(targ, self.fs.Entry):
1383 def Precious(self, *targets):
1386 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1391 def Repository(self, *dirs, **kw):
1392 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1393 apply(self.fs.Repository, dirs, kw)
1395 def Scanner(self, *args, **kw):
1398 if SCons.Util.is_String(arg):
1399 arg = self.subst(arg)
1401 nkw = self.subst_kw(kw)
1402 return apply(SCons.Scanner.Scanner, nargs, nkw)
1404 def SConsignFile(self, name=".sconsign", dbm_module=None):
1405 if not name is None:
1406 name = self.subst(name)
1407 if not os.path.isabs(name):
1408 name = os.path.join(str(self.fs.SConstruct_dir), name)
1409 SCons.SConsign.File(name, dbm_module)
1411 def SideEffect(self, side_effect, target):
1412 """Tell scons that side_effects are built as side
1413 effects of building targets."""
1414 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1415 targets = self.arg2nodes(target, self.fs.Entry)
1417 for side_effect in side_effects:
1418 if side_effect.multiple_side_effect_has_builder():
1419 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1420 side_effect.add_source(targets)
1421 side_effect.side_effect = 1
1422 self.Precious(side_effect)
1423 for target in targets:
1424 target.side_effects.append(side_effect)
1427 def SourceCode(self, entry, builder):
1428 """Arrange for a source code builder for (part of) a tree."""
1429 entries = self.arg2nodes(entry, self.fs.Entry)
1430 for entry in entries:
1431 entry.set_src_builder(builder)
1434 def SourceSignatures(self, type):
1435 type = self.subst(type)
1437 import SCons.Sig.MD5
1438 self._calc_module = SCons.Sig.MD5
1439 elif type == 'timestamp':
1440 import SCons.Sig.TimeStamp
1441 self._calc_module = SCons.Sig.TimeStamp
1443 raise UserError, "Unknown source signature type '%s'"%type
1445 def Split(self, arg):
1446 """This function converts a string or list into a list of strings
1447 or Nodes. This makes things easier for users by allowing files to
1448 be specified as a white-space separated list to be split.
1449 The input rules are:
1450 - A single string containing names separated by spaces. These will be
1451 split apart at the spaces.
1452 - A single Node instance
1453 - A list containing either strings or Node instances. Any strings
1454 in the list are not split at spaces.
1455 In all cases, the function returns a list of Nodes and strings."""
1456 if SCons.Util.is_List(arg):
1457 return map(self.subst, arg)
1458 elif SCons.Util.is_String(arg):
1459 return string.split(self.subst(arg))
1461 return [self.subst(arg)]
1463 def TargetSignatures(self, type):
1464 type = self.subst(type)
1466 self._build_signature = 1
1467 elif type == 'content':
1468 self._build_signature = 0
1470 raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1472 def Value(self, value):
1475 return SCons.Node.Python.Value(value)
1477 class OverrideEnvironment(Base):
1478 """A proxy that overrides variables in a wrapped construction
1479 environment by returning values from an overrides dictionary in
1480 preference to values from the underlying subject environment.
1482 This is a lightweight (I hope) proxy that passes through most use of
1483 attributes to the underlying Environment.Base class, but has just
1484 enough additional methods defined to act like a real construction
1485 environment with overridden values. It can wrap either a Base
1486 construction environment, or another OverrideEnvironment, which
1487 can in turn nest arbitrary OverrideEnvironments...
1489 Note that we do *not* call the underlying base class
1490 (SubsitutionEnvironment) initialization, because we get most of those
1491 from proxying the attributes of the subject construction environment.
1492 But because we subclass SubstitutionEnvironment, this class also
1493 has inherited arg2nodes() and subst*() methods; those methods can't
1494 be proxied because they need *this* object's methods to fetch the
1495 values from the overrides dictionary.
1498 if SCons.Memoize.use_memoizer:
1499 __metaclass__ = SCons.Memoize.Memoized_Metaclass
1501 def __init__(self, subject, overrides={}):
1502 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
1503 self.__dict__['__subject'] = subject
1504 self.__dict__['overrides'] = overrides
1506 # Methods that make this class act like a proxy.
1507 def __getattr__(self, name):
1508 return getattr(self.__dict__['__subject'], name)
1509 def __setattr__(self, name, value):
1510 return setattr(self.__dict__['__subject'], name, value)
1512 # Methods that make this class act like a dictionary.
1513 def __getitem__(self, key):
1515 return self.__dict__['overrides'][key]
1517 return self.__dict__['__subject'].__getitem__(key)
1518 def __setitem__(self, key, value):
1519 if not SCons.Util.is_valid_construction_var(key):
1520 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
1521 self.__dict__['overrides'][key] = value
1522 def __delitem__(self, key):
1524 del self.__dict__['overrides'][key]
1530 result = self.__dict__['__subject'].__delitem__(key)
1536 def get(self, key, default=None):
1537 """Emulates the get() method of dictionaries."""
1539 return self.__dict__['overrides'][key]
1541 return self.__dict__['__subject'].get(key, default)
1542 def has_key(self, key):
1544 self.__dict__['overrides'][key]
1547 return self.__dict__['__subject'].has_key(key)
1548 def Dictionary(self):
1549 """Emulates the items() method of dictionaries."""
1550 d = self.__dict__['__subject'].Dictionary().copy()
1551 d.update(self.__dict__['overrides'])
1554 """Emulates the items() method of dictionaries."""
1555 return self.Dictionary().items()
1557 # Overridden private construction environment methods.
1558 def _update(self, dict):
1559 """Update an environment's values directly, bypassing the normal
1560 checks that occur when users try to set items.
1562 self.__dict__['overrides'].update(dict)
1565 return self.__dict__['__subject'].gvars()
1568 lvars = self.__dict__['__subject'].lvars()
1569 lvars.update(self.__dict__['overrides'])
1572 # Overridden public construction environment methods.
1573 def Replace(self, **kw):
1574 kw = copy_non_reserved_keywords(kw)
1575 self.__dict__['overrides'].update(our_deepcopy(kw))
1577 # The entry point that will be used by the external world
1578 # to refer to a construction environment. This allows the wrapper
1579 # interface to extend a construction environment for its own purposes
1580 # by subclassing SCons.Environment.Base and then assigning the
1581 # class to SCons.Environment.Environment.
1585 # An entry point for returning a proxy subclass instance that overrides
1586 # the subst*() methods so they don't actually perform construction
1587 # variable substitution. This is specifically intended to be the shim
1588 # layer in between global function calls (which don't want construction
1589 # variable substitution) and the DefaultEnvironment() (which would
1590 # substitute variables if left to its own devices)."""
1592 # We have to wrap this in a function that allows us to delay definition of
1593 # the class until it's necessary, so that when it subclasses Environment
1594 # it will pick up whatever Environment subclass the wrapper interface
1595 # might have assigned to SCons.Environment.Environment.
1597 def NoSubstitutionProxy(subject):
1598 class _NoSubstitutionProxy(Environment):
1599 def __init__(self, subject):
1600 self.__dict__['__subject'] = subject
1601 def __getattr__(self, name):
1602 return getattr(self.__dict__['__subject'], name)
1603 def __setattr__(self, name, value):
1604 return setattr(self.__dict__['__subject'], name, value)
1605 def raw_to_mode(self, dict):
1613 def subst(self, string, *args, **kwargs):
1615 def subst_kw(self, kw, *args, **kwargs):
1617 def subst_list(self, string, *args, **kwargs):
1618 nargs = (string, self,) + args
1621 self.raw_to_mode(nkw)
1622 return apply(SCons.Subst.scons_subst_list, nargs, nkw)
1623 def subst_target_source(self, string, *args, **kwargs):
1624 nargs = (string, self,) + args
1627 self.raw_to_mode(nkw)
1628 return apply(SCons.Subst.scons_subst, nargs, nkw)
1629 return _NoSubstitutionProxy(subject)
1631 if SCons.Memoize.use_old_memoization():
1633 class Base(SCons.Memoize.Memoizer, _Base):
1634 def __init__(self, *args, **kw):
1635 SCons.Memoize.Memoizer.__init__(self)
1636 apply(_Base.__init__, (self,)+args, kw)