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
70 # Pull UserError into the global name space for the benefit of
71 # Environment().SourceSignatures(), which has some import statements
72 # which seem to mess up its ability to reference SCons directly.
73 UserError = SCons.Errors.UserError
75 def installFunc(target, source, env):
76 """Install a source file into a target using the function specified
77 as the INSTALL construction variable."""
79 install = env['INSTALL']
81 raise SCons.Errors.UserError('Missing INSTALL construction variable.')
82 return install(target[0].path, source[0].path, env)
84 def installString(target, source, env):
85 return 'Install file: "%s" as "%s"' % (source[0], target[0])
87 installAction = SCons.Action.Action(installFunc, installString)
89 InstallBuilder = SCons.Builder.Builder(action=installAction,
90 name='InstallBuilder')
92 def alias_builder(env, target, source):
95 AliasBuilder = SCons.Builder.Builder(action = alias_builder,
96 target_factory = SCons.Node.Alias.default_ans.Alias,
97 source_factory = SCons.Node.FS.default_fs.Entry,
103 """deepcopy lists and dictionaries, and just copy the reference
104 for everything else."""
105 if SCons.Util.is_Dict(x):
108 copy[key] = our_deepcopy(x[key])
109 elif SCons.Util.is_List(x):
110 copy = map(our_deepcopy, x)
112 copy = x.__class__(copy)
113 except AttributeError:
119 def apply_tools(env, tools, toolpath):
122 # Filter out null tools from the list.
123 for tool in filter(None, tools):
124 if SCons.Util.is_List(tool) or type(tool)==type(()):
126 toolargs = tool[1] # should be a dict of kw args
127 tool = apply(env.Tool, (toolname, toolpath), toolargs)
129 env.Tool(tool, toolpath)
131 # These names are controlled by SCons; users should never set or override
132 # them. This warning can optionally be turned off, but scons will still
133 # ignore the illegal variable names even if it's off.
134 reserved_construction_var_names = \
135 ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
137 def copy_non_reserved_keywords(dict):
138 result = our_deepcopy(dict)
139 for k in result.keys():
140 if k in reserved_construction_var_names:
141 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
142 "Ignoring attempt to set reserved variable `%s'" % k)
146 class BuilderWrapper:
147 """Wrapper class that associates an environment with a Builder at
149 def __init__(self, env, builder):
151 self.builder = builder
153 def __call__(self, target=None, source=_null, *args, **kw):
157 if not target is None and not SCons.Util.is_List(target):
159 if not source is None and not SCons.Util.is_List(source):
161 return apply(self.builder, (self.env, target, source) + args, kw)
163 # This allows a Builder to be executed directly
164 # through the Environment to which it's attached.
165 # In practice, we shouldn't need this, because
166 # builders actually get executed through a Node.
167 # But we do have a unit test for this, and can't
168 # yet rule out that it would be useful in the
169 # future, so leave it for now.
170 def execute(self, **kw):
172 apply(self.builder.execute, (), kw)
174 class BuilderDict(UserDict):
175 """This is a dictionary-like class used by an Environment to hold
176 the Builders. We need to do this because every time someone changes
177 the Builders in the Environment's BUILDERS dictionary, we must
178 update the Environment's attributes."""
179 def __init__(self, dict, env):
180 # Set self.env before calling the superclass initialization,
181 # because it will end up calling our other methods, which will
182 # need to point the values in this dictionary to self.env.
184 UserDict.__init__(self, dict)
186 def __setitem__(self, item, val):
187 UserDict.__setitem__(self, item, val)
189 self.setenvattr(item, val)
190 except AttributeError:
191 # Have to catch this because sometimes __setitem__ gets
192 # called out of __init__, when we don't have an env
193 # attribute yet, nor do we want one!
196 def setenvattr(self, item, val):
197 """Set the corresponding environment attribute for this Builder.
199 If the value is already a BuilderWrapper, we pull the builder
200 out of it and make another one, so that making a copy of an
201 existing BuilderDict is guaranteed separate wrappers for each
202 Builder + Environment pair."""
204 builder = val.builder
205 except AttributeError:
207 setattr(self.env, item, BuilderWrapper(self.env, builder))
209 def __delitem__(self, item):
210 UserDict.__delitem__(self, item)
211 delattr(self.env, item)
213 def update(self, dict):
214 for i, v in dict.items():
215 self.__setitem__(i, v)
217 class SubstitutionEnvironment:
218 """Base class for different flavors of construction environments.
220 This class contains a minimal set of methods that handle contruction
221 variable expansion and conversion of strings to Nodes, which may or
222 may not be actually useful as a stand-alone class. Which methods
223 ended up in this class is pretty arbitrary right now. They're
224 basically the ones which we've empirically determined are common to
225 the different construction environment subclasses, and most of the
226 others that use or touch the underlying dictionary of construction
229 Eventually, this class should contain all the methods that we
230 determine are necessary for a "minimal" interface to the build engine.
231 A full "native Python" SCons environment has gotten pretty heavyweight
232 with all of the methods and Tools and construction variables we've
233 jammed in there, so it would be nice to have a lighter weight
234 alternative for interfaces that don't need all of the bells and
235 whistles. (At some point, we'll also probably rename this class
236 "Base," since that more reflects what we want this class to become,
237 but because we've released comments that tell people to subclass
238 Environment.Base to create their own flavors of construction
239 environment, we'll save that for a future refactoring when this
240 class actually becomes useful.)
242 def __init__(self, **kw):
243 """Initialization of an underlying SubstitutionEnvironment class.
245 if __debug__: logInstanceCreation(self)
246 self.fs = SCons.Node.FS.default_fs
247 self.ans = SCons.Node.Alias.default_ans
248 self.lookup_list = SCons.Node.arg2nodes_lookups
249 self._dict = kw.copy()
250 self._dict['__env__'] = self
252 def __cmp__(self, other):
253 # Since an Environment now has an '__env__' construction variable
254 # that refers to itself, delete that variable to avoid infinite
255 # loops when comparing the underlying dictionaries in some Python
256 # versions (*cough* 1.5.2 *cough*)...
257 sdict = self._dict.copy()
259 odict = other._dict.copy()
261 return cmp(sdict, odict)
263 def __delitem__(self, key):
266 def __getitem__(self, key):
267 return self._dict[key]
269 def __setitem__(self, key, value):
270 if key in reserved_construction_var_names:
271 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
272 "Ignoring attempt to set reserved variable `%s'" % key)
273 elif key == 'BUILDERS':
279 self._dict[key] = BuilderDict(kwbd, self)
280 self._dict[key].update(value)
281 elif key == 'SCANNERS':
282 self._dict[key] = value
283 self.scanner_map_delete()
285 if not SCons.Util.is_valid_construction_var(key):
286 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
287 self._dict[key] = value
289 def get(self, key, default=None):
290 "Emulates the get() method of dictionaries."""
291 return self._dict.get(key, default)
293 def has_key(self, key):
294 return self._dict.has_key(key)
297 "Emulates the items() method of dictionaries."""
298 result = self._dict.items()
299 result = filter(lambda t: t[0] != '__env__', result)
302 def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
303 if node_factory is _null:
304 node_factory = self.fs.File
305 if lookup_list is _null:
306 lookup_list = self.lookup_list
311 if SCons.Util.is_List(args):
312 args = SCons.Util.flatten(args)
318 if SCons.Util.is_String(v):
320 for l in lookup_list:
325 if SCons.Util.is_String(n):
326 n = self.subst(n, raw=1)
329 if SCons.Util.is_List(n):
334 v = node_factory(self.subst(v, raw=1))
335 if SCons.Util.is_List(v):
350 def subst(self, string, raw=0, target=None, source=None, conv=None):
351 """Recursively interpolates construction variables from the
352 Environment into the specified string, returning the expanded
353 result. Construction variables are specified by a $ prefix
354 in the string and begin with an initial underscore or
355 alphabetic character followed by any number of underscores
356 or alphanumeric characters. The construction variable names
357 may be surrounded by curly braces to separate the name from
362 return SCons.Util.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
364 def subst_kw(self, kw, raw=0, target=None, source=None):
366 for k, v in kw.items():
367 k = self.subst(k, raw, target, source)
368 if SCons.Util.is_String(v):
369 v = self.subst(v, raw, target, source)
373 def subst_list(self, string, raw=0, target=None, source=None, conv=None):
374 """Calls through to SCons.Util.scons_subst_list(). See
375 the documentation for that function."""
378 return SCons.Util.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
380 def subst_path(self, path):
381 """Substitute a path list, turning EntryProxies into Nodes
382 and leaving Nodes (and other objects) as-is."""
384 if not SCons.Util.is_List(path):
388 """This is the "string conversion" routine that we have our
389 substitutions use to return Nodes, not strings. This relies
390 on the fact that an EntryProxy object has a get() method that
391 returns the underlying Node that it wraps, which is a bit of
392 architectural dependence that we might need to break or modify
393 in the future in response to additional requirements."""
396 except AttributeError:
404 if SCons.Util.is_String(p):
405 p = self.subst(p, conv=s)
406 if SCons.Util.is_List(p):
410 # We have an object plus a string, or multiple
411 # objects that we need to smush together. No choice
412 # but to make them into a string.
413 p = string.join(map(SCons.Util.to_String, p), '')
419 subst_target_source = subst
421 def Override(self, overrides):
423 Produce a modified environment whose variables are overriden by
424 the overrides dictionaries. "overrides" is a dictionary that
425 will override the variables of this environment.
427 This function is much more efficient than Copy() or creating
428 a new Environment because it doesn't copy the construction
429 environment dictionary, it just wraps the underlying construction
430 environment, and doesn't even create a wrapper object if there
434 o = copy_non_reserved_keywords(overrides)
436 for key, value in o.items():
437 overrides[key] = SCons.Util.scons_subst_once(value, self, key)
439 env = OverrideEnvironment(self, overrides)
444 class Base(SubstitutionEnvironment):
445 """Base class for "real" construction Environments. These are the
446 primary objects used to communicate dependency and construction
447 information to the build engine.
449 Keyword arguments supplied when the construction Environment
450 is created are construction variables used to initialize the
454 #######################################################################
455 # This is THE class for interacting with the SCons build engine,
456 # and it contains a lot of stuff, so we're going to try to keep this
457 # a little organized by grouping the methods.
458 #######################################################################
460 #######################################################################
461 # Methods that make an Environment act like a dictionary. These have
462 # the expected standard names for Python mapping objects. Note that
463 # we don't actually make an Environment a subclass of UserDict for
464 # performance reasons. Note also that we only supply methods for
465 # dictionary functionality that we actually need and use.
466 #######################################################################
475 Initialization of a basic SCons construction environment,
476 including setting up special construction variables like BUILDER,
477 PLATFORM, etc., and searching for and applying available Tools.
479 Note that we do *not* call the underlying base class
480 (SubsitutionEnvironment) initialization, because we need to
481 initialize things in a very specific order that doesn't work
482 with the much simpler base class initialization.
484 if __debug__: logInstanceCreation(self)
485 self.fs = SCons.Node.FS.default_fs
486 self.ans = SCons.Node.Alias.default_ans
487 self.lookup_list = SCons.Node.arg2nodes_lookups
488 self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
490 self._dict['__env__'] = self
491 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
494 platform = self._dict.get('PLATFORM', None)
496 platform = SCons.Platform.Platform()
497 if SCons.Util.is_String(platform):
498 platform = SCons.Platform.Platform(platform)
499 self._dict['PLATFORM'] = str(platform)
502 # Apply the passed-in variables before calling the tools,
503 # because they may use some of them:
504 apply(self.Replace, (), kw)
506 # Update the environment with the customizable options
507 # before calling the tools, since they may use some of the options:
512 tools = self._dict.get('TOOLS', None)
515 apply_tools(self, tools, toolpath)
517 # Reapply the passed in variables after calling the tools,
518 # since they should overide anything set by the tools:
519 apply(self.Replace, (), kw)
521 # Update the environment with the customizable options
522 # after calling the tools, since they should override anything
527 #######################################################################
528 # Utility methods that are primarily for internal use by SCons.
529 # These begin with lower-case letters.
530 #######################################################################
532 def get_calculator(self):
534 return self._calculator
535 except AttributeError:
537 module = self._calc_module
538 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
539 except AttributeError:
540 # Note that we're calling get_calculator() here, so the
541 # DefaultEnvironment() must have a _calc_module attribute
542 # to avoid infinite recursion.
543 c = SCons.Defaults.DefaultEnvironment().get_calculator()
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_scanner(self, skey):
556 """Find the appropriate scanner given a key (usually a file suffix).
559 sm = self.scanner_map
560 except AttributeError:
562 scanners = self._dict['SCANNERS']
564 self.scanner_map = {}
567 self.scanner_map = sm = {}
568 # Reverse the scanner list so that, if multiple scanners
569 # claim they can scan the same suffix, earlier scanners
570 # in the list will overwrite later scanners, so that
571 # the result looks like a "first match" to the user.
572 if not SCons.Util.is_List(scanners):
573 scanners = [scanners]
575 for scanner in scanners:
576 for k in scanner.get_skeys(self):
583 def scanner_map_delete(self, kw=None):
584 """Delete the cached scanner map (if we need to).
586 if not kw is None and not kw.has_key('SCANNERS'):
590 except AttributeError:
593 def _update(self, dict):
594 """Update an environment's values directly, bypassing the normal
595 checks that occur when users try to set items.
597 self._dict.update(dict)
599 def use_build_signature(self):
601 return self._build_signature
602 except AttributeError:
603 b = SCons.Defaults.DefaultEnvironment()._build_signature
604 self._build_signature = b
607 #######################################################################
608 # Public methods for manipulating an Environment. These begin with
609 # upper-case letters. The essential characteristic of methods in
610 # this section is that they do *not* have corresponding same-named
611 # global functions. For example, a stand-alone Append() function
612 # makes no sense, because Append() is all about appending values to
613 # an Environment's construction variables.
614 #######################################################################
616 def Append(self, **kw):
617 """Append values to existing construction variables
620 kw = copy_non_reserved_keywords(kw)
621 for key, val in kw.items():
622 # It would be easier on the eyes to write this using
623 # "continue" statements whenever we finish processing an item,
624 # but Python 1.5.2 apparently doesn't let you use "continue"
625 # within try:-except: blocks, so we have to nest our code.
627 orig = self._dict[key]
629 # No existing variable in the environment, so just set
630 # it to the new value.
631 self._dict[key] = val
634 # Most straightforward: just try to add them
635 # together. This will work in most cases, when the
636 # original and new values are of compatible types.
637 self._dict[key] = orig + val
640 # Try to update a dictionary value with another.
641 # If orig isn't a dictionary, it won't have an
642 # update() method; if val isn't a dictionary,
643 # it won't have a keys() method. Either way,
644 # it's an AttributeError.
646 except AttributeError:
648 # Check if the original is a list.
649 add_to_orig = orig.append
650 except AttributeError:
651 # The original isn't a list, but the new
652 # value is (by process of elimination),
653 # so insert the original in the new value
654 # (if there's one to insert) and replace
655 # the variable with it.
658 self._dict[key] = val
660 # The original is a list, so append the new
661 # value to it (if there's a value to append).
664 self.scanner_map_delete(kw)
666 def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
667 """Append path elements to the path 'name' in the 'ENV'
668 dictionary for this environment. Will only add any particular
669 path once, and will normpath and normcase all paths to help
670 assure this. This can also handle the case where the env
671 variable is a list instead of a string.
675 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
676 orig = self._dict[envname][name]
678 nv = SCons.Util.AppendPath(orig, newpath, sep)
680 if not self._dict.has_key(envname):
681 self._dict[envname] = {}
683 self._dict[envname][name] = nv
685 def AppendUnique(self, **kw):
686 """Append values to existing construction variables
687 in an Environment, if they're not already there.
689 kw = copy_non_reserved_keywords(kw)
690 for key, val in kw.items():
691 if not self._dict.has_key(key):
692 self._dict[key] = val
693 elif SCons.Util.is_Dict(self._dict[key]) and \
694 SCons.Util.is_Dict(val):
695 self._dict[key].update(val)
696 elif SCons.Util.is_List(val):
698 if not SCons.Util.is_List(dk):
700 val = filter(lambda x, dk=dk: x not in dk, val)
701 self._dict[key] = dk + val
704 if SCons.Util.is_List(dk):
706 self._dict[key] = dk + val
708 self._dict[key] = self._dict[key] + val
709 self.scanner_map_delete(kw)
711 def Copy(self, tools=[], toolpath=[], **kw):
712 """Return a copy of a construction Environment. The
713 copy is like a Python "deep copy"--that is, independent
714 copies are made recursively of each objects--except that
715 a reference is copied when an object is not deep-copyable
716 (like a function). There are no references to any mutable
717 objects in the original Environment.
719 clone = copy.copy(self)
720 clone._dict = our_deepcopy(self._dict)
721 clone['__env__'] = clone
723 cbd = clone._dict['BUILDERS']
724 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
728 apply_tools(clone, tools, toolpath)
730 # Apply passed-in variables after the new tools.
731 kw = copy_non_reserved_keywords(kw)
733 for key, value in kw.items():
734 new[key] = SCons.Util.scons_subst_once(value, self, key)
735 apply(clone.Replace, (), new)
736 if __debug__: logInstanceCreation(self, 'EnvironmentCopy')
739 def Detect(self, progs):
740 """Return the first available program in progs.
742 if not SCons.Util.is_List(progs):
745 path = self.WhereIs(prog)
749 def Dictionary(self, *args):
752 dlist = map(lambda x, s=self: s._dict[x], args)
757 def Dump(self, key = None):
759 Using the standard Python pretty printer, dump the contents of the
760 scons build environment to stdout.
762 If the key passed in is anything other than None, then that will
763 be used as an index into the build environment dictionary and
764 whatever is found there will be fed into the pretty printer. Note
765 that this key is case sensitive.
768 pp = pprint.PrettyPrinter(indent=2)
770 dict = self.Dictionary(key)
772 dict = self.Dictionary()
773 return pp.pformat(dict)
775 def FindIxes(self, paths, prefix, suffix):
777 Search a list of paths for something that matches the prefix and suffix.
779 paths - the list of paths or nodes.
780 prefix - construction variable for the prefix.
781 suffix - construction variable for the suffix.
784 suffix = self.subst('$'+suffix)
785 prefix = self.subst('$'+prefix)
788 dir,name = os.path.split(str(path))
789 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
792 def ParseConfig(self, command, function=None):
794 Use the specified function to parse the output of the command
795 in order to modify the current environment. The 'command' can
796 be a string or a list of strings representing a command and
797 it's arguments. 'Function' is an optional argument that takes
798 the environment and the output of the command. If no function is
799 specified, the output will be treated as the output of a typical
800 'X-config' command (i.e. gtk-config) and used to append to the
801 ASFLAGS, CCFLAGS, CPPFLAGS, CPPPATH, LIBPATH, LIBS, LINKFLAGS
802 and CCFLAGS variables.
805 # the default parse function
806 def parse_conf(env, output, fs=self.fs):
817 params = string.split(output)
818 append_next_arg_to='' # for multi-word args
820 if append_next_arg_to:
821 dict[append_next_arg_to].append(arg)
822 append_next_arg_to = ''
824 dict['LIBS'].append(fs.File(arg))
825 elif arg[:2] == '-L':
827 dict['LIBPATH'].append(arg[2:])
829 append_next_arg_to = 'LIBPATH'
830 elif arg[:2] == '-l':
832 dict['LIBS'].append(arg[2:])
834 append_next_arg_to = 'LIBS'
835 elif arg[:2] == '-I':
837 dict['CPPPATH'].append(arg[2:])
839 append_next_arg_to = 'CPPPATH'
840 elif arg[:4] == '-Wa,':
841 dict['ASFLAGS'].append(arg)
842 elif arg[:4] == '-Wl,':
843 dict['LINKFLAGS'].append(arg)
844 elif arg[:4] == '-Wp,':
845 dict['CPPFLAGS'].append(arg)
846 elif arg == '-framework':
847 dict['LINKFLAGS'].append(arg)
848 append_next_arg_to='LINKFLAGS'
849 elif arg == '-mno-cygwin':
850 dict['CCFLAGS'].append(arg)
851 dict['LINKFLAGS'].append(arg)
852 elif arg == '-mwindows':
853 dict['LINKFLAGS'].append(arg)
854 elif arg == '-pthread':
855 dict['CCFLAGS'].append(arg)
856 dict['LINKFLAGS'].append(arg)
858 dict['CCFLAGS'].append(arg)
859 apply(env.Append, (), dict)
862 function = parse_conf
863 if type(command) is type([]):
864 command = string.join(command)
865 command = self.subst(command)
866 return function(self, os.popen(command).read())
868 def ParseDepends(self, filename, must_exist=None):
870 Parse a mkdep-style file for explicit dependencies. This is
871 completely abusable, and should be unnecessary in the "normal"
872 case of proper SCons configuration, but it may help make
873 the transition from a Make hierarchy easier for some people
874 to swallow. It can also be genuinely useful when using a tool
875 that can write a .d file, but for which writing a scanner would
879 fp = open(filename, 'r')
884 for line in SCons.Util.LogicalLines(fp).readlines():
888 target, depends = string.split(line, ':', 1)
889 except (AttributeError, TypeError, ValueError):
890 # Python 1.5.2 throws TypeError if line isn't a string,
891 # Python 2.x throws AttributeError because it tries
892 # to call line.splite(). Either can throw ValueError
893 # if the line doesn't split into two or more elements.
896 self.Depends(string.split(target), string.split(depends))
898 def Platform(self, platform):
899 platform = self.subst(platform)
900 return SCons.Platform.Platform(platform)(self)
902 def Prepend(self, **kw):
903 """Prepend values to existing construction variables
906 kw = copy_non_reserved_keywords(kw)
907 for key, val in kw.items():
908 # It would be easier on the eyes to write this using
909 # "continue" statements whenever we finish processing an item,
910 # but Python 1.5.2 apparently doesn't let you use "continue"
911 # within try:-except: blocks, so we have to nest our code.
913 orig = self._dict[key]
915 # No existing variable in the environment, so just set
916 # it to the new value.
917 self._dict[key] = val
920 # Most straightforward: just try to add them
921 # together. This will work in most cases, when the
922 # original and new values are of compatible types.
923 self._dict[key] = val + orig
926 # Try to update a dictionary value with another.
927 # If orig isn't a dictionary, it won't have an
928 # update() method; if val isn't a dictionary,
929 # it won't have a keys() method. Either way,
930 # it's an AttributeError.
932 except AttributeError:
934 # Check if the added value is a list.
935 add_to_val = val.append
936 except AttributeError:
937 # The added value isn't a list, but the
938 # original is (by process of elimination),
939 # so insert the the new value in the original
940 # (if there's one to insert).
944 # The added value is a list, so append
945 # the original to it (if there's a value
949 self._dict[key] = val
950 self.scanner_map_delete(kw)
952 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
953 """Prepend path elements to the path 'name' in the 'ENV'
954 dictionary for this environment. Will only add any particular
955 path once, and will normpath and normcase all paths to help
956 assure this. This can also handle the case where the env
957 variable is a list instead of a string.
961 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
962 orig = self._dict[envname][name]
964 nv = SCons.Util.PrependPath(orig, newpath, sep)
966 if not self._dict.has_key(envname):
967 self._dict[envname] = {}
969 self._dict[envname][name] = nv
971 def PrependUnique(self, **kw):
972 """Append values to existing construction variables
973 in an Environment, if they're not already there.
975 kw = copy_non_reserved_keywords(kw)
976 for key, val in kw.items():
977 if not self._dict.has_key(key):
978 self._dict[key] = val
979 elif SCons.Util.is_Dict(self._dict[key]) and \
980 SCons.Util.is_Dict(val):
981 self._dict[key].update(val)
982 elif SCons.Util.is_List(val):
984 if not SCons.Util.is_List(dk):
986 val = filter(lambda x, dk=dk: x not in dk, val)
987 self._dict[key] = val + dk
990 if SCons.Util.is_List(dk):
992 self._dict[key] = val + dk
994 self._dict[key] = val + dk
995 self.scanner_map_delete(kw)
997 def Replace(self, **kw):
998 """Replace existing construction variables in an Environment
999 with new construction variables and/or values.
1002 kwbd = our_deepcopy(kw['BUILDERS'])
1004 self.__setitem__('BUILDERS', kwbd)
1007 kw = copy_non_reserved_keywords(kw)
1008 self._dict.update(our_deepcopy(kw))
1009 self.scanner_map_delete(kw)
1011 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1013 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1015 env - Environment used to interpolate variables.
1016 path - the path that will be modified.
1017 old_prefix - construction variable for the old prefix.
1018 old_suffix - construction variable for the old suffix.
1019 new_prefix - construction variable for the new prefix.
1020 new_suffix - construction variable for the new suffix.
1022 old_prefix = self.subst('$'+old_prefix)
1023 old_suffix = self.subst('$'+old_suffix)
1025 new_prefix = self.subst('$'+new_prefix)
1026 new_suffix = self.subst('$'+new_suffix)
1028 dir,name = os.path.split(str(path))
1029 if name[:len(old_prefix)] == old_prefix:
1030 name = name[len(old_prefix):]
1031 if name[-len(old_suffix):] == old_suffix:
1032 name = name[:-len(old_suffix)]
1033 return os.path.join(dir, new_prefix+name+new_suffix)
1035 def SetDefault(self, **kw):
1037 if self._dict.has_key(k):
1039 apply(self.Replace, (), kw)
1041 def Tool(self, tool, toolpath=[], **kw):
1042 if SCons.Util.is_String(tool):
1043 tool = self.subst(tool)
1044 toolpath = map(self.subst, toolpath)
1045 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
1048 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1049 """Find prog in the path.
1053 path = self['ENV']['PATH']
1056 elif SCons.Util.is_String(path):
1057 path = self.subst(path)
1060 pathext = self['ENV']['PATHEXT']
1063 elif SCons.Util.is_String(pathext):
1064 pathext = self.subst(pathext)
1065 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1066 if path: return path
1069 #######################################################################
1070 # Public methods for doing real "SCons stuff" (manipulating
1071 # dependencies, setting attributes on targets, etc.). These begin
1072 # with upper-case letters. The essential characteristic of methods
1073 # in this section is that they all *should* have corresponding
1074 # same-named global functions.
1075 #######################################################################
1077 def Action(self, *args, **kw):
1078 nargs = self.subst(args)
1079 nkw = self.subst_kw(kw)
1080 return apply(SCons.Action.Action, nargs, nkw)
1082 def AddPreAction(self, files, action):
1083 nodes = self.arg2nodes(files, self.fs.Entry)
1084 action = SCons.Action.Action(action)
1086 n.add_pre_action(action)
1089 def AddPostAction(self, files, action):
1090 nodes = self.arg2nodes(files, self.fs.Entry)
1091 action = SCons.Action.Action(action)
1093 n.add_post_action(action)
1096 def Alias(self, target, source=[], action=None, **kw):
1097 tlist = self.arg2nodes(target, self.ans.Alias)
1098 if not SCons.Util.is_List(source):
1100 source = filter(None, source)
1104 # There are no source files and no action, so just
1105 # return a target list of classic Alias Nodes, without
1106 # any builder. The externally visible effect is that
1107 # this will make the wrapping Script.BuildTask class
1108 # say that there's "Nothing to be done" for this Alias,
1109 # instead of that it's "up to date."
1112 # No action, but there are sources. Re-call all the target
1113 # builders to add the sources to each target.
1116 bld = t.get_builder(AliasBuilder)
1117 result.extend(bld(self, t, source))
1120 action = SCons.Action.Action(action)
1121 nkw = self.subst_kw(kw)
1122 nkw['source_factory'] = self.fs.Entry
1124 nkw['action'] = action
1125 bld = apply(SCons.Builder.Builder, (), nkw)
1127 # Apply the Builder separately to each target so that the Aliases
1128 # stay separate. If we did one "normal" Builder call with the
1129 # whole target list, then all of the target Aliases would be
1130 # associated under a single Executor.
1133 # Calling the convert() method will cause a new Executor to be
1134 # created from scratch, so we have to explicitly initialize
1135 # it with the target's existing sources, plus our new ones,
1136 # so nothing gets lost.
1138 if b is None or b is AliasBuilder:
1141 nkw['action'] = b.action + action
1142 b = apply(SCons.Builder.Builder, (), nkw)
1144 result.extend(b(self, t, t.sources + source))
1147 def AlwaysBuild(self, *targets):
1150 tlist.extend(self.arg2nodes(t, self.fs.File))
1152 t.set_always_build()
1155 def BuildDir(self, build_dir, src_dir, duplicate=1):
1156 build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
1157 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1158 self.fs.BuildDir(build_dir, src_dir, duplicate)
1160 def Builder(self, **kw):
1161 nkw = self.subst_kw(kw)
1162 return apply(SCons.Builder.Builder, [], nkw)
1164 def CacheDir(self, path):
1165 self.fs.CacheDir(self.subst(path))
1167 def Clean(self, targets, files):
1169 tlist = self.arg2nodes(targets, self.fs.Entry)
1170 flist = self.arg2nodes(files, self.fs.Entry)
1173 CleanTargets[t].extend(flist)
1175 CleanTargets[t] = flist
1177 def Configure(self, *args, **kw):
1180 nargs = nargs + self.subst_list(args)[0]
1181 nkw = self.subst_kw(kw)
1182 nkw['called_from_env_method'] = 1
1184 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1187 return apply(SCons.SConf.SConf, nargs, nkw)
1189 def Command(self, target, source, action, **kw):
1190 """Builds the supplied target files from the supplied
1191 source files using the supplied action. Action may
1192 be any type that the Builder constructor will accept
1194 bld = SCons.Builder.Builder(action = action,
1195 source_factory = self.fs.Entry)
1196 return apply(bld, (self, target, source), kw)
1198 def Depends(self, target, dependency):
1199 """Explicity specify that 'target's depend on 'dependency'."""
1200 tlist = self.arg2nodes(target, self.fs.Entry)
1201 dlist = self.arg2nodes(dependency, self.fs.Entry)
1203 t.add_dependency(dlist)
1206 def Dir(self, name, *args, **kw):
1209 return apply(self.fs.Dir, (self.subst(name),) + args, kw)
1211 def Environment(self, **kw):
1212 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1214 def Execute(self, action, *args, **kw):
1215 """Directly execute an action through an Environment
1217 action = apply(self.Action, (action,) + args, kw)
1218 return action([], [], self)
1220 def File(self, name, *args, **kw):
1223 return apply(self.fs.File, (self.subst(name),) + args, kw)
1225 def FindFile(self, file, dirs):
1226 file = self.subst(file)
1227 nodes = self.arg2nodes(dirs, self.fs.Dir)
1228 return SCons.Node.FS.find_file(file, nodes, self.fs.File)
1230 def Flatten(self, sequence):
1231 return SCons.Util.flatten(sequence)
1233 def GetBuildPath(self, files):
1234 result = map(str, self.arg2nodes(files, self.fs.Entry))
1235 if SCons.Util.is_List(files):
1240 def Ignore(self, target, dependency):
1241 """Ignore a dependency."""
1242 tlist = self.arg2nodes(target, self.fs.Entry)
1243 dlist = self.arg2nodes(dependency, self.fs.Entry)
1248 def Install(self, dir, source):
1249 """Install specified files in the given directory."""
1251 dnodes = self.arg2nodes(dir, self.fs.Dir)
1253 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)
1255 sources = self.arg2nodes(source, self.fs.File)
1257 if SCons.Util.is_List(source):
1258 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))
1260 raise SCons.Errors.UserError, "Source `%s' of Install() is not a file. Install() source must be one or more files." % str(source)
1262 for dnode in dnodes:
1264 target = self.fs.File(src.name, dnode)
1265 tgt.extend(InstallBuilder(self, target, src))
1268 def InstallAs(self, target, source):
1269 """Install sources as targets."""
1270 sources = self.arg2nodes(source, self.fs.File)
1271 targets = self.arg2nodes(target, self.fs.File)
1273 for src, tgt in map(lambda x, y: (x, y), sources, targets):
1274 result.extend(InstallBuilder(self, tgt, src))
1277 def Literal(self, string):
1278 return SCons.Util.Literal(string)
1280 def Local(self, *targets):
1282 for targ in targets:
1283 if isinstance(targ, SCons.Node.Node):
1287 for t in self.arg2nodes(targ, self.fs.Entry):
1292 def Precious(self, *targets):
1295 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1300 def Repository(self, *dirs, **kw):
1301 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1302 apply(self.fs.Repository, dirs, kw)
1304 def Scanner(self, *args, **kw):
1307 if SCons.Util.is_String(arg):
1308 arg = self.subst(arg)
1310 nkw = self.subst_kw(kw)
1311 return apply(SCons.Scanner.Scanner, nargs, nkw)
1313 def SConsignFile(self, name=".sconsign", dbm_module=None):
1314 name = self.subst(name)
1315 if not os.path.isabs(name):
1316 name = os.path.join(str(self.fs.SConstruct_dir), name)
1317 SCons.SConsign.File(name, dbm_module)
1319 def SideEffect(self, side_effect, target):
1320 """Tell scons that side_effects are built as side
1321 effects of building targets."""
1322 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1323 targets = self.arg2nodes(target, self.fs.Entry)
1325 for side_effect in side_effects:
1326 if side_effect.multiple_side_effect_has_builder():
1327 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1328 side_effect.add_source(targets)
1329 side_effect.side_effect = 1
1330 self.Precious(side_effect)
1331 for target in targets:
1332 target.side_effects.append(side_effect)
1335 def SourceCode(self, entry, builder):
1336 """Arrange for a source code builder for (part of) a tree."""
1337 entries = self.arg2nodes(entry, self.fs.Entry)
1338 for entry in entries:
1339 entry.set_src_builder(builder)
1342 def SourceSignatures(self, type):
1343 type = self.subst(type)
1345 import SCons.Sig.MD5
1346 self._calc_module = SCons.Sig.MD5
1347 elif type == 'timestamp':
1348 import SCons.Sig.TimeStamp
1349 self._calc_module = SCons.Sig.TimeStamp
1351 raise UserError, "Unknown source signature type '%s'"%type
1353 def Split(self, arg):
1354 """This function converts a string or list into a list of strings
1355 or Nodes. This makes things easier for users by allowing files to
1356 be specified as a white-space separated list to be split.
1357 The input rules are:
1358 - A single string containing names separated by spaces. These will be
1359 split apart at the spaces.
1360 - A single Node instance
1361 - A list containing either strings or Node instances. Any strings
1362 in the list are not split at spaces.
1363 In all cases, the function returns a list of Nodes and strings."""
1364 if SCons.Util.is_List(arg):
1365 return map(self.subst, arg)
1366 elif SCons.Util.is_String(arg):
1367 return string.split(self.subst(arg))
1369 return [self.subst(arg)]
1371 def TargetSignatures(self, type):
1372 type = self.subst(type)
1374 self._build_signature = 1
1375 elif type == 'content':
1376 self._build_signature = 0
1378 raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1380 def Value(self, value):
1383 return SCons.Node.Python.Value(value)
1385 class OverrideEnvironment(SubstitutionEnvironment):
1386 """A proxy that overrides variables in a wrapped construction
1387 environment by returning values from an overrides dictionary in
1388 preference to values from the underlying subject environment.
1390 This is a lightweight (I hope) proxy that passes through most use of
1391 attributes to the underlying Environment.Base class, but has just
1392 enough additional methods defined to act like a real construction
1393 environment with overridden values. It can wrap either a Base
1394 construction environment, or another OverrideEnvironment, which
1395 can in turn nest arbitrary OverrideEnvironments...
1397 Note that we do *not* call the underlying base class
1398 (SubsitutionEnvironment) initialization, because we get most of those
1399 from proxying the attributes of the subject construction environment.
1400 But because we subclass SubstitutionEnvironment, this class also
1401 has inherited arg2nodes() and subst*() methods; those methods can't
1402 be proxied because they need *this* object's methods to fetch the
1403 values from the overrides dictionary.
1405 def __init__(self, subject, overrides={}):
1406 if __debug__: logInstanceCreation(self, 'OverrideEnvironment')
1407 self.__dict__['__subject'] = subject
1408 self.__dict__['overrides'] = overrides
1409 self.__dict__['overrides']['__env__'] = self
1411 # Methods that make this class act like a proxy.
1412 def __getattr__(self, name):
1413 return getattr(self.__dict__['__subject'], name)
1414 def __setattr__(self, name, value):
1415 return setattr(self.__dict__['__subject'], name, value)
1417 # Methods that make this class act like a dictionary.
1418 def __getitem__(self, key):
1420 return self.__dict__['overrides'][key]
1422 return self.__dict__['__subject'].__getitem__(key)
1423 def __setitem__(self, key, value):
1424 if not SCons.Util.is_valid_construction_var(key):
1425 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
1426 self.__dict__['overrides'][key] = value
1427 def __delitem__(self, key):
1429 del self.__dict__['overrides'][key]
1432 return self.__dict__['__subject'].__delitem__(key)
1433 def get(self, key, default=None):
1434 """Emulates the get() method of dictionaries."""
1436 return self.__dict__['overrides'][key]
1438 return self.__dict__['__subject'].get(key, default)
1439 def has_key(self, key):
1441 self.__dict__['overrides'][key]
1444 return self.__dict__['__subject'].has_key(key)
1446 """Emulates the items() method of dictionaries."""
1447 return self.Dictionary().items()
1449 # Overridden private construction environment methods.
1450 def _update(self, dict):
1451 """Update an environment's values directly, bypassing the normal
1452 checks that occur when users try to set items.
1454 self.__dict__['overrides'].update(dict)
1457 return self.__dict__['__subject'].gvars()
1460 lvars = self.__dict__['__subject'].lvars()
1461 lvars.update(self.__dict__['overrides'])
1464 # Overridden public construction environment methods.
1465 def Replace(self, **kw):
1466 kw = copy_non_reserved_keywords(kw)
1467 self.__dict__['overrides'].update(our_deepcopy(kw))
1469 # The entry point that will be used by the external world
1470 # to refer to a construction environment. This allows the wrapper
1471 # interface to extend a construction environment for its own purposes
1472 # by subclassing SCons.Environment.Base and then assigning the
1473 # class to SCons.Environment.Environment.
1477 # An entry point for returning a proxy subclass instance that overrides
1478 # the subst*() methods so they don't actually perform construction
1479 # variable substitution. This is specifically intended to be the shim
1480 # layer in between global function calls (which don't want construction
1481 # variable substitution) and the DefaultEnvironment() (which would
1482 # substitute variables if left to its own devices)."""
1484 # We have to wrap this in a function that allows us to delay definition of
1485 # the class until it's necessary, so that when it subclasses Environment
1486 # it will pick up whatever Environment subclass the wrapper interface
1487 # might have assigned to SCons.Environment.Environment.
1489 def NoSubstitutionProxy(subject):
1490 class _NoSubstitutionProxy(Environment):
1491 def __init__(self, subject):
1492 self.__dict__['__subject'] = subject
1493 def __getattr__(self, name):
1494 return getattr(self.__dict__['__subject'], name)
1495 def __setattr__(self, name, value):
1496 return setattr(self.__dict__['__subject'], name, value)
1497 def raw_to_mode(self, dict):
1505 def subst(self, string, *args, **kwargs):
1507 def subst_kw(self, kw, *args, **kwargs):
1509 def subst_list(self, string, *args, **kwargs):
1510 nargs = (string, self,) + args
1513 self.raw_to_mode(nkw)
1514 return apply(SCons.Util.scons_subst_list, nargs, nkw)
1515 def subst_target_source(self, string, *args, **kwargs):
1516 nargs = (string, self,) + args
1519 self.raw_to_mode(nkw)
1520 return apply(SCons.Util.scons_subst, nargs, nkw)
1521 return _NoSubstitutionProxy(subject)