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)
218 """Base class for construction Environments. These are
219 the primary objects used to communicate dependency and
220 construction information to the build engine.
222 Keyword arguments supplied when the construction Environment
223 is created are construction variables used to initialize the
227 #######################################################################
228 # This is THE class for interacting with the SCons build engine,
229 # and it contains a lot of stuff, so we're going to try to keep this
230 # a little organized by grouping the methods.
231 #######################################################################
233 #######################################################################
234 # Methods that make an Environment act like a dictionary. These have
235 # the expected standard names for Python mapping objects. Note that
236 # we don't actually make an Environment a subclass of UserDict for
237 # performance reasons. Note also that we only supply methods for
238 # dictionary functionality that we actually need and use.
239 #######################################################################
247 if __debug__: logInstanceCreation(self)
248 self.fs = SCons.Node.FS.default_fs
249 self.ans = SCons.Node.Alias.default_ans
250 self.lookup_list = SCons.Node.arg2nodes_lookups
251 self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
253 self._dict['__env__'] = self
254 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
257 platform = self._dict.get('PLATFORM', None)
259 platform = SCons.Platform.Platform()
260 if SCons.Util.is_String(platform):
261 platform = SCons.Platform.Platform(platform)
262 self._dict['PLATFORM'] = str(platform)
265 # Apply the passed-in variables before calling the tools,
266 # because they may use some of them:
267 apply(self.Replace, (), kw)
269 # Update the environment with the customizable options
270 # before calling the tools, since they may use some of the options:
275 tools = self._dict.get('TOOLS', None)
278 apply_tools(self, tools, toolpath)
280 # Reapply the passed in variables after calling the tools,
281 # since they should overide anything set by the tools:
282 apply(self.Replace, (), kw)
284 # Update the environment with the customizable options
285 # after calling the tools, since they should override anything
290 def __cmp__(self, other):
291 # Since an Environment now has an '__env__' construction variable
292 # that refers to itself, delete that variable to avoid infinite
293 # loops when comparing the underlying dictionaries in some Python
294 # versions (*cough* 1.5.2 *cough*)...
295 sdict = self._dict.copy()
297 odict = other._dict.copy()
299 return cmp(sdict, odict)
301 def __getitem__(self, key):
302 return self._dict[key]
304 def __setitem__(self, key, value):
305 if key in reserved_construction_var_names:
306 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
307 "Ignoring attempt to set reserved variable `%s'" % key)
308 elif key == 'BUILDERS':
314 self._dict[key] = BuilderDict(kwbd, self)
315 self._dict[key].update(value)
316 elif key == 'SCANNERS':
317 self._dict[key] = value
318 self.scanner_map_delete()
320 if not SCons.Util.is_valid_construction_var(key):
321 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
322 self._dict[key] = value
324 def __delitem__(self, key):
328 "Emulates the items() method of dictionaries."""
329 return self._dict.items()
331 def has_key(self, key):
332 return self._dict.has_key(key)
334 def get(self, key, default=None):
335 "Emulates the get() method of dictionaries."""
336 return self._dict.get(key, default)
338 #######################################################################
339 # Utility methods that are primarily for internal use by SCons.
340 # These begin with lower-case letters. Note that the subst() method
341 # is actually already out of the closet and used by people.
342 #######################################################################
344 def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
345 if node_factory is _null:
346 node_factory = self.fs.File
347 if lookup_list is _null:
348 lookup_list = self.lookup_list
353 if SCons.Util.is_List(args):
354 args = SCons.Util.flatten(args)
360 if SCons.Util.is_String(v):
362 for l in lookup_list:
367 if SCons.Util.is_String(n):
368 n = self.subst(n, raw=1)
371 if SCons.Util.is_List(n):
376 v = node_factory(self.subst(v, raw=1))
377 if SCons.Util.is_List(v):
386 def get_calculator(self):
388 return self._calculator
389 except AttributeError:
391 module = self._calc_module
392 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
393 except AttributeError:
394 # Note that we're calling get_calculator() here, so the
395 # DefaultEnvironment() must have a _calc_module attribute
396 # to avoid infinite recursion.
397 c = SCons.Defaults.DefaultEnvironment().get_calculator()
401 def get_builder(self, name):
402 """Fetch the builder with the specified name from the environment.
405 return self._dict['BUILDERS'][name]
409 def get_scanner(self, skey):
410 """Find the appropriate scanner given a key (usually a file suffix).
413 sm = self.scanner_map
414 except AttributeError:
416 scanners = self._dict['SCANNERS']
418 self.scanner_map = {}
421 self.scanner_map = sm = {}
422 # Reverse the scanner list so that, if multiple scanners
423 # claim they can scan the same suffix, earlier scanners
424 # in the list will overwrite later scanners, so that
425 # the result looks like a "first match" to the user.
426 if not SCons.Util.is_List(scanners):
427 scanners = [scanners]
429 for scanner in scanners:
430 for k in scanner.get_skeys(self):
437 def scanner_map_delete(self, kw=None):
438 """Delete the cached scanner map (if we need to).
440 if not kw is None and not kw.has_key('SCANNERS'):
444 except AttributeError:
447 def subst(self, string, raw=0, target=None, source=None, dict=None, conv=None):
448 """Recursively interpolates construction variables from the
449 Environment into the specified string, returning the expanded
450 result. Construction variables are specified by a $ prefix
451 in the string and begin with an initial underscore or
452 alphabetic character followed by any number of underscores
453 or alphanumeric characters. The construction variable names
454 may be surrounded by curly braces to separate the name from
457 return SCons.Util.scons_subst(string, self, raw, target, source, dict, conv)
459 def subst_kw(self, kw, raw=0, target=None, source=None, dict=None):
461 for k, v in kw.items():
462 k = self.subst(k, raw, target, source, dict)
463 if SCons.Util.is_String(v):
464 v = self.subst(v, raw, target, source, dict)
468 def subst_list(self, string, raw=0, target=None, source=None, dict=None, conv=None):
469 """Calls through to SCons.Util.scons_subst_list(). See
470 the documentation for that function."""
471 return SCons.Util.scons_subst_list(string, self, raw, target, source, dict, conv)
474 def subst_path(self, path):
475 """Substitute a path list, turning EntryProxies into Nodes
476 and leaving Nodes (and other objects) as-is."""
478 if not SCons.Util.is_List(path):
482 """This is the "string conversion" routine that we have our
483 substitutions use to return Nodes, not strings. This relies
484 on the fact that an EntryProxy object has a get() method that
485 returns the underlying Node that it wraps, which is a bit of
486 architectural dependence that we might need to break or modify
487 in the future in response to additional requirements."""
490 except AttributeError:
498 if SCons.Util.is_String(p):
499 p = self.subst(p, conv=s)
500 if SCons.Util.is_List(p):
504 # We have an object plus a string, or multiple
505 # objects that we need to smush together. No choice
506 # but to make them into a string.
507 p = string.join(map(SCons.Util.to_String, p), '')
513 subst_target_source = subst
515 def _update(self, dict):
516 """Update an environment's values directly, bypassing the normal
517 checks that occur when users try to set items.
519 self._dict.update(dict)
521 def use_build_signature(self):
523 return self._build_signature
524 except AttributeError:
525 b = SCons.Defaults.DefaultEnvironment()._build_signature
526 self._build_signature = b
529 #######################################################################
530 # Public methods for manipulating an Environment. These begin with
531 # upper-case letters. The essential characteristic of methods in
532 # this section is that they do *not* have corresponding same-named
533 # global functions. For example, a stand-alone Append() function
534 # makes no sense, because Append() is all about appending values to
535 # an Environment's construction variables.
536 #######################################################################
538 def Append(self, **kw):
539 """Append values to existing construction variables
542 kw = copy_non_reserved_keywords(kw)
543 for key, val in kw.items():
544 # It would be easier on the eyes to write this using
545 # "continue" statements whenever we finish processing an item,
546 # but Python 1.5.2 apparently doesn't let you use "continue"
547 # within try:-except: blocks, so we have to nest our code.
549 orig = self._dict[key]
551 # No existing variable in the environment, so just set
552 # it to the new value.
553 self._dict[key] = val
556 # Most straightforward: just try to add them
557 # together. This will work in most cases, when the
558 # original and new values are of compatible types.
559 self._dict[key] = orig + val
562 # Try to update a dictionary value with another.
563 # If orig isn't a dictionary, it won't have an
564 # update() method; if val isn't a dictionary,
565 # it won't have a keys() method. Either way,
566 # it's an AttributeError.
568 except AttributeError:
570 # Check if the original is a list.
571 add_to_orig = orig.append
572 except AttributeError:
573 # The original isn't a list, but the new
574 # value is (by process of elimination),
575 # so insert the original in the new value
576 # (if there's one to insert) and replace
577 # the variable with it.
580 self._dict[key] = val
582 # The original is a list, so append the new
583 # value to it (if there's a value to append).
586 self.scanner_map_delete(kw)
588 def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
589 """Append path elements to the path 'name' in the 'ENV'
590 dictionary for this environment. Will only add any particular
591 path once, and will normpath and normcase all paths to help
592 assure this. This can also handle the case where the env
593 variable is a list instead of a string.
597 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
598 orig = self._dict[envname][name]
600 nv = SCons.Util.AppendPath(orig, newpath, sep)
602 if not self._dict.has_key(envname):
603 self._dict[envname] = {}
605 self._dict[envname][name] = nv
607 def AppendUnique(self, **kw):
608 """Append values to existing construction variables
609 in an Environment, if they're not already there.
611 kw = copy_non_reserved_keywords(kw)
612 for key, val in kw.items():
613 if not self._dict.has_key(key):
614 self._dict[key] = val
615 elif SCons.Util.is_Dict(self._dict[key]) and \
616 SCons.Util.is_Dict(val):
617 self._dict[key].update(val)
618 elif SCons.Util.is_List(val):
620 if not SCons.Util.is_List(dk):
622 val = filter(lambda x, dk=dk: x not in dk, val)
623 self._dict[key] = dk + val
626 if SCons.Util.is_List(dk):
628 self._dict[key] = dk + val
630 self._dict[key] = self._dict[key] + val
631 self.scanner_map_delete(kw)
633 def Copy(self, tools=[], toolpath=[], **kw):
634 """Return a copy of a construction Environment. The
635 copy is like a Python "deep copy"--that is, independent
636 copies are made recursively of each objects--except that
637 a reference is copied when an object is not deep-copyable
638 (like a function). There are no references to any mutable
639 objects in the original Environment.
641 clone = copy.copy(self)
642 clone._dict = our_deepcopy(self._dict)
643 clone['__env__'] = clone
645 cbd = clone._dict['BUILDERS']
646 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
650 apply_tools(clone, tools, toolpath)
652 # Apply passed-in variables after the new tools.
653 kw = copy_non_reserved_keywords(kw)
655 for key, value in kw.items():
656 new[key] = SCons.Util.scons_subst_once(value, self, key)
657 apply(clone.Replace, (), new)
658 if __debug__: logInstanceCreation(self, 'EnvironmentCopy')
661 def Detect(self, progs):
662 """Return the first available program in progs.
664 if not SCons.Util.is_List(progs):
667 path = self.WhereIs(prog)
671 def Dictionary(self, *args):
674 dlist = map(lambda x, s=self: s._dict[x], args)
679 def Dump(self, key = None):
681 Using the standard Python pretty printer, dump the contents of the
682 scons build environment to stdout.
684 If the key passed in is anything other than None, then that will
685 be used as an index into the build environment dictionary and
686 whatever is found there will be fed into the pretty printer. Note
687 that this key is case sensitive.
690 pp = pprint.PrettyPrinter(indent=2)
692 dict = self.Dictionary(key)
694 dict = self.Dictionary()
695 return pp.pformat(dict)
697 def FindIxes(self, paths, prefix, suffix):
699 Search a list of paths for something that matches the prefix and suffix.
701 paths - the list of paths or nodes.
702 prefix - construction variable for the prefix.
703 suffix - construction variable for the suffix.
706 suffix = self.subst('$'+suffix)
707 prefix = self.subst('$'+prefix)
710 dir,name = os.path.split(str(path))
711 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
714 def Override(self, overrides):
716 Produce a modified environment whose variables
717 are overriden by the overrides dictionaries.
719 overrides - a dictionary that will override
720 the variables of this environment.
722 This function is much more efficient than Copy()
723 or creating a new Environment because it doesn't do
724 a deep copy of the dictionary, and doesn't do a copy
725 at all if there are no overrides.
729 env = copy.copy(self)
730 env._dict = copy.copy(self._dict)
732 overrides = copy_non_reserved_keywords(overrides)
734 for key, value in overrides.items():
735 new[key] = SCons.Util.scons_subst_once(value, self, key)
736 env._dict.update(new)
737 if __debug__: logInstanceCreation(self, 'EnvironmentOverride')
742 def ParseConfig(self, command, function=None):
744 Use the specified function to parse the output of the command
745 in order to modify the current environment. The 'command' can
746 be a string or a list of strings representing a command and
747 it's arguments. 'Function' is an optional argument that takes
748 the environment and the output of the command. If no function is
749 specified, the output will be treated as the output of a typical
750 'X-config' command (i.e. gtk-config) and used to append to the
751 ASFLAGS, CCFLAGS, CPPFLAGS, CPPPATH, LIBPATH, LIBS, LINKFLAGS
752 and CCFLAGS variables.
755 # the default parse function
756 def parse_conf(env, output, fs=self.fs):
767 params = string.split(output)
768 append_next_arg_to='' # for multi-word args
770 if append_next_arg_to:
771 dict[append_next_arg_to].append(arg)
772 append_next_arg_to = ''
774 dict['LIBS'].append(fs.File(arg))
775 elif arg[:2] == '-L':
777 dict['LIBPATH'].append(arg[2:])
779 append_next_arg_to = 'LIBPATH'
780 elif arg[:2] == '-l':
782 dict['LIBS'].append(arg[2:])
784 append_next_arg_to = 'LIBS'
785 elif arg[:2] == '-I':
787 dict['CPPPATH'].append(arg[2:])
789 append_next_arg_to = 'CPPPATH'
790 elif arg[:4] == '-Wa,':
791 dict['ASFLAGS'].append(arg)
792 elif arg[:4] == '-Wl,':
793 dict['LINKFLAGS'].append(arg)
794 elif arg[:4] == '-Wp,':
795 dict['CPPFLAGS'].append(arg)
796 elif arg == '-framework':
797 dict['LINKFLAGS'].append(arg)
798 append_next_arg_to='LINKFLAGS'
799 elif arg == '-mno-cygwin':
800 dict['CCFLAGS'].append(arg)
801 dict['LINKFLAGS'].append(arg)
802 elif arg == '-mwindows':
803 dict['LINKFLAGS'].append(arg)
804 elif arg == '-pthread':
805 dict['CCFLAGS'].append(arg)
806 dict['LINKFLAGS'].append(arg)
808 dict['CCFLAGS'].append(arg)
809 apply(env.Append, (), dict)
812 function = parse_conf
813 if type(command) is type([]):
814 command = string.join(command)
815 command = self.subst(command)
816 return function(self, os.popen(command).read())
818 def ParseDepends(self, filename, must_exist=None):
820 Parse a mkdep-style file for explicit dependencies. This is
821 completely abusable, and should be unnecessary in the "normal"
822 case of proper SCons configuration, but it may help make
823 the transition from a Make hierarchy easier for some people
824 to swallow. It can also be genuinely useful when using a tool
825 that can write a .d file, but for which writing a scanner would
829 fp = open(filename, 'r')
834 for line in SCons.Util.LogicalLines(fp).readlines():
838 target, depends = string.split(line, ':', 1)
842 self.Depends(string.split(target), string.split(depends))
844 def Platform(self, platform):
845 platform = self.subst(platform)
846 return SCons.Platform.Platform(platform)(self)
848 def Prepend(self, **kw):
849 """Prepend values to existing construction variables
852 kw = copy_non_reserved_keywords(kw)
853 for key, val in kw.items():
854 # It would be easier on the eyes to write this using
855 # "continue" statements whenever we finish processing an item,
856 # but Python 1.5.2 apparently doesn't let you use "continue"
857 # within try:-except: blocks, so we have to nest our code.
859 orig = self._dict[key]
861 # No existing variable in the environment, so just set
862 # it to the new value.
863 self._dict[key] = val
866 # Most straightforward: just try to add them
867 # together. This will work in most cases, when the
868 # original and new values are of compatible types.
869 self._dict[key] = val + orig
872 # Try to update a dictionary value with another.
873 # If orig isn't a dictionary, it won't have an
874 # update() method; if val isn't a dictionary,
875 # it won't have a keys() method. Either way,
876 # it's an AttributeError.
878 except AttributeError:
880 # Check if the added value is a list.
881 add_to_val = val.append
882 except AttributeError:
883 # The added value isn't a list, but the
884 # original is (by process of elimination),
885 # so insert the the new value in the original
886 # (if there's one to insert).
890 # The added value is a list, so append
891 # the original to it (if there's a value
895 self._dict[key] = val
896 self.scanner_map_delete(kw)
898 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
899 """Prepend path elements to the path 'name' in the 'ENV'
900 dictionary for this environment. Will only add any particular
901 path once, and will normpath and normcase all paths to help
902 assure this. This can also handle the case where the env
903 variable is a list instead of a string.
907 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
908 orig = self._dict[envname][name]
910 nv = SCons.Util.PrependPath(orig, newpath, sep)
912 if not self._dict.has_key(envname):
913 self._dict[envname] = {}
915 self._dict[envname][name] = nv
917 def PrependUnique(self, **kw):
918 """Append values to existing construction variables
919 in an Environment, if they're not already there.
921 kw = copy_non_reserved_keywords(kw)
922 for key, val in kw.items():
923 if not self._dict.has_key(key):
924 self._dict[key] = val
925 elif SCons.Util.is_Dict(self._dict[key]) and \
926 SCons.Util.is_Dict(val):
927 self._dict[key].update(val)
928 elif SCons.Util.is_List(val):
930 if not SCons.Util.is_List(dk):
932 val = filter(lambda x, dk=dk: x not in dk, val)
933 self._dict[key] = val + dk
936 if SCons.Util.is_List(dk):
938 self._dict[key] = val + dk
940 self._dict[key] = val + dk
941 self.scanner_map_delete(kw)
943 def Replace(self, **kw):
944 """Replace existing construction variables in an Environment
945 with new construction variables and/or values.
948 kwbd = our_deepcopy(kw['BUILDERS'])
950 self.__setitem__('BUILDERS', kwbd)
953 kw = copy_non_reserved_keywords(kw)
954 self._dict.update(our_deepcopy(kw))
955 self.scanner_map_delete(kw)
957 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
959 Replace old_prefix with new_prefix and old_suffix with new_suffix.
961 env - Environment used to interpolate variables.
962 path - the path that will be modified.
963 old_prefix - construction variable for the old prefix.
964 old_suffix - construction variable for the old suffix.
965 new_prefix - construction variable for the new prefix.
966 new_suffix - construction variable for the new suffix.
968 old_prefix = self.subst('$'+old_prefix)
969 old_suffix = self.subst('$'+old_suffix)
971 new_prefix = self.subst('$'+new_prefix)
972 new_suffix = self.subst('$'+new_suffix)
974 dir,name = os.path.split(str(path))
975 if name[:len(old_prefix)] == old_prefix:
976 name = name[len(old_prefix):]
977 if name[-len(old_suffix):] == old_suffix:
978 name = name[:-len(old_suffix)]
979 return os.path.join(dir, new_prefix+name+new_suffix)
981 def SetDefault(self, **kw):
983 if self._dict.has_key(k):
985 apply(self.Replace, (), kw)
987 def Tool(self, tool, toolpath=[], **kw):
988 if SCons.Util.is_String(tool):
989 tool = self.subst(tool)
990 toolpath = map(self.subst, toolpath)
991 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
994 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
995 """Find prog in the path.
999 path = self['ENV']['PATH']
1002 elif SCons.Util.is_String(path):
1003 path = self.subst(path)
1006 pathext = self['ENV']['PATHEXT']
1009 elif SCons.Util.is_String(pathext):
1010 pathext = self.subst(pathext)
1011 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1012 if path: return path
1015 #######################################################################
1016 # Public methods for doing real "SCons stuff" (manipulating
1017 # dependencies, setting attributes on targets, etc.). These begin
1018 # with upper-case letters. The essential characteristic of methods
1019 # in this section is that they all *should* have corresponding
1020 # same-named global functions.
1021 #######################################################################
1023 def Action(self, *args, **kw):
1024 nargs = self.subst(args)
1025 nkw = self.subst_kw(kw)
1026 return apply(SCons.Action.Action, nargs, nkw)
1028 def AddPreAction(self, files, action):
1029 nodes = self.arg2nodes(files, self.fs.Entry)
1030 action = SCons.Action.Action(action)
1032 n.add_pre_action(action)
1035 def AddPostAction(self, files, action):
1036 nodes = self.arg2nodes(files, self.fs.Entry)
1037 action = SCons.Action.Action(action)
1039 n.add_post_action(action)
1042 def Alias(self, target, source=[], action=None, **kw):
1043 tlist = self.arg2nodes(target, self.ans.Alias)
1044 if not SCons.Util.is_List(source):
1046 source = filter(None, source)
1050 # There are no source files and no action, so just
1051 # return a target list of classic Alias Nodes, without
1052 # any builder. The externally visible effect is that
1053 # this will make the wrapping Script.BuildTask class
1054 # say that there's "Nothing to be done" for this Alias,
1055 # instead of that it's "up to date."
1058 # No action, but there are sources. Re-call all the target
1059 # builders to add the sources to each target.
1062 bld = t.get_builder(AliasBuilder)
1063 result.extend(bld(self, t, source))
1066 action = SCons.Action.Action(action)
1067 nkw = self.subst_kw(kw)
1068 nkw['source_factory'] = self.fs.Entry
1070 nkw['action'] = action
1071 bld = apply(SCons.Builder.Builder, (), nkw)
1073 # Apply the Builder separately to each target so that the Aliases
1074 # stay separate. If we did one "normal" Builder call with the
1075 # whole target list, then all of the target Aliases would be
1076 # associated under a single Executor.
1079 # Calling the convert() method will cause a new Executor to be
1080 # created from scratch, so we have to explicitly initialize
1081 # it with the target's existing sources, plus our new ones,
1082 # so nothing gets lost.
1084 if b is None or b is AliasBuilder:
1087 nkw['action'] = b.action + action
1088 b = apply(SCons.Builder.Builder, (), nkw)
1090 result.extend(b(self, t, t.sources + source))
1093 def AlwaysBuild(self, *targets):
1096 tlist.extend(self.arg2nodes(t, self.fs.File))
1098 t.set_always_build()
1101 def BuildDir(self, build_dir, src_dir, duplicate=1):
1102 build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
1103 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1104 self.fs.BuildDir(build_dir, src_dir, duplicate)
1106 def Builder(self, **kw):
1107 nkw = self.subst_kw(kw)
1108 return apply(SCons.Builder.Builder, [], nkw)
1110 def CacheDir(self, path):
1111 self.fs.CacheDir(self.subst(path))
1113 def Clean(self, targets, files):
1115 tlist = self.arg2nodes(targets, self.fs.Entry)
1116 flist = self.arg2nodes(files, self.fs.Entry)
1119 CleanTargets[t].extend(flist)
1121 CleanTargets[t] = flist
1123 def Configure(self, *args, **kw):
1126 nargs = nargs + self.subst_list(args)[0]
1127 nkw = self.subst_kw(kw)
1128 nkw['called_from_env_method'] = 1
1130 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1133 return apply(SCons.SConf.SConf, nargs, nkw)
1135 def Command(self, target, source, action, **kw):
1136 """Builds the supplied target files from the supplied
1137 source files using the supplied action. Action may
1138 be any type that the Builder constructor will accept
1140 bld = SCons.Builder.Builder(action = action,
1141 source_factory = self.fs.Entry)
1142 return apply(bld, (self, target, source), kw)
1144 def Depends(self, target, dependency):
1145 """Explicity specify that 'target's depend on 'dependency'."""
1146 tlist = self.arg2nodes(target, self.fs.Entry)
1147 dlist = self.arg2nodes(dependency, self.fs.Entry)
1149 t.add_dependency(dlist)
1152 def Dir(self, name, *args, **kw):
1155 return apply(self.fs.Dir, (self.subst(name),) + args, kw)
1157 def Environment(self, **kw):
1158 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1160 def Execute(self, action, *args, **kw):
1161 """Directly execute an action through an Environment
1163 action = apply(self.Action, (action,) + args, kw)
1164 return action([], [], self)
1166 def File(self, name, *args, **kw):
1169 return apply(self.fs.File, (self.subst(name),) + args, kw)
1171 def FindFile(self, file, dirs):
1172 file = self.subst(file)
1173 nodes = self.arg2nodes(dirs, self.fs.Dir)
1174 return SCons.Node.FS.find_file(file, nodes, self.fs.File)
1176 def Flatten(self, sequence):
1177 return SCons.Util.flatten(sequence)
1179 def GetBuildPath(self, files):
1180 result = map(str, self.arg2nodes(files, self.fs.Entry))
1181 if SCons.Util.is_List(files):
1186 def Ignore(self, target, dependency):
1187 """Ignore a dependency."""
1188 tlist = self.arg2nodes(target, self.fs.Entry)
1189 dlist = self.arg2nodes(dependency, self.fs.Entry)
1194 def Install(self, dir, source):
1195 """Install specified files in the given directory."""
1197 dnodes = self.arg2nodes(dir, self.fs.Dir)
1199 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)
1201 sources = self.arg2nodes(source, self.fs.File)
1203 if SCons.Util.is_List(source):
1204 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))
1206 raise SCons.Errors.UserError, "Source `%s' of Install() is not a file. Install() source must be one or more files." % str(source)
1208 for dnode in dnodes:
1210 target = self.fs.File(src.name, dnode)
1211 tgt.extend(InstallBuilder(self, target, src))
1214 def InstallAs(self, target, source):
1215 """Install sources as targets."""
1216 sources = self.arg2nodes(source, self.fs.File)
1217 targets = self.arg2nodes(target, self.fs.File)
1219 for src, tgt in map(lambda x, y: (x, y), sources, targets):
1220 result.extend(InstallBuilder(self, tgt, src))
1223 def Literal(self, string):
1224 return SCons.Util.Literal(string)
1226 def Local(self, *targets):
1228 for targ in targets:
1229 if isinstance(targ, SCons.Node.Node):
1233 for t in self.arg2nodes(targ, self.fs.Entry):
1238 def Precious(self, *targets):
1241 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1246 def Repository(self, *dirs, **kw):
1247 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1248 apply(self.fs.Repository, dirs, kw)
1250 def Scanner(self, *args, **kw):
1253 if SCons.Util.is_String(arg):
1254 arg = self.subst(arg)
1256 nkw = self.subst_kw(kw)
1257 return apply(SCons.Scanner.Scanner, nargs, nkw)
1259 def SConsignFile(self, name=".sconsign", dbm_module=None):
1260 name = self.subst(name)
1261 if not os.path.isabs(name):
1262 name = os.path.join(str(self.fs.SConstruct_dir), name)
1263 SCons.SConsign.File(name, dbm_module)
1265 def SideEffect(self, side_effect, target):
1266 """Tell scons that side_effects are built as side
1267 effects of building targets."""
1268 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1269 targets = self.arg2nodes(target, self.fs.Entry)
1271 for side_effect in side_effects:
1272 if side_effect.multiple_side_effect_has_builder():
1273 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1274 side_effect.add_source(targets)
1275 side_effect.side_effect = 1
1276 self.Precious(side_effect)
1277 for target in targets:
1278 target.side_effects.append(side_effect)
1281 def SourceCode(self, entry, builder):
1282 """Arrange for a source code builder for (part of) a tree."""
1283 entries = self.arg2nodes(entry, self.fs.Entry)
1284 for entry in entries:
1285 entry.set_src_builder(builder)
1288 def SourceSignatures(self, type):
1289 type = self.subst(type)
1291 import SCons.Sig.MD5
1292 self._calc_module = SCons.Sig.MD5
1293 elif type == 'timestamp':
1294 import SCons.Sig.TimeStamp
1295 self._calc_module = SCons.Sig.TimeStamp
1297 raise UserError, "Unknown source signature type '%s'"%type
1299 def Split(self, arg):
1300 """This function converts a string or list into a list of strings
1301 or Nodes. This makes things easier for users by allowing files to
1302 be specified as a white-space separated list to be split.
1303 The input rules are:
1304 - A single string containing names separated by spaces. These will be
1305 split apart at the spaces.
1306 - A single Node instance
1307 - A list containing either strings or Node instances. Any strings
1308 in the list are not split at spaces.
1309 In all cases, the function returns a list of Nodes and strings."""
1310 if SCons.Util.is_List(arg):
1311 return map(self.subst, arg)
1312 elif SCons.Util.is_String(arg):
1313 return string.split(self.subst(arg))
1315 return [self.subst(arg)]
1317 def TargetSignatures(self, type):
1318 type = self.subst(type)
1320 self._build_signature = 1
1321 elif type == 'content':
1322 self._build_signature = 0
1324 raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1326 def Value(self, value):
1329 return SCons.Node.Python.Value(value)
1331 # The entry point that will be used by the external world
1332 # to refer to a construction environment. This allows the wrapper
1333 # interface to extend a construction environment for its own purposes
1334 # by subclassing SCons.Environment.Base and then assigning the
1335 # class to SCons.Environment.Environment.
1339 # An entry point for returning a proxy subclass instance that overrides
1340 # the subst*() methods so they don't actually perform construction
1341 # variable substitution. This is specifically intended to be the shim
1342 # layer in between global function calls (which don't want construction
1343 # variable substitution) and the DefaultEnvironment() (which would
1344 # substitute variables if left to its own devices)."""
1346 # We have to wrap this in a function that allows us to delay definition of
1347 # the class until it's necessary, so that when it subclasses Environment
1348 # it will pick up whatever Environment subclass the wrapper interface
1349 # might have assigned to SCons.Environment.Environment.
1351 def NoSubstitutionProxy(subject):
1352 class _NoSubstitutionProxy(Environment):
1353 def __init__(self, subject):
1354 self.__dict__['__subject'] = subject
1355 def __getattr__(self, name):
1356 return getattr(self.__dict__['__subject'], name)
1357 def __setattr__(self, name, value):
1358 return setattr(self.__dict__['__subject'], name, value)
1359 def raw_to_mode(self, dict):
1367 def subst(self, string, *args, **kwargs):
1369 def subst_kw(self, kw, *args, **kwargs):
1371 def subst_list(self, string, *args, **kwargs):
1372 nargs = (string, self,) + args
1375 self.raw_to_mode(nkw)
1376 return apply(SCons.Util.scons_subst_list, nargs, nkw)
1377 def subst_target_source(self, string, *args, **kwargs):
1378 nargs = (string, self,) + args
1381 self.raw_to_mode(nkw)
1382 return apply(SCons.Util.scons_subst, nargs, nkw)
1383 return _NoSubstitutionProxy(subject)