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)
91 def alias_builder(env, target, source):
94 AliasBuilder = SCons.Builder.Builder(action = alias_builder,
95 target_factory = SCons.Node.Alias.default_ans.Alias,
96 source_factory = SCons.Node.FS.default_fs.Entry,
100 """deepcopy lists and dictionaries, and just copy the reference
101 for everything else."""
102 if SCons.Util.is_Dict(x):
105 copy[key] = our_deepcopy(x[key])
106 elif SCons.Util.is_List(x):
107 copy = map(our_deepcopy, x)
109 copy = x.__class__(copy)
110 except AttributeError:
116 def apply_tools(env, tools, toolpath):
118 # Filter out null tools from the list.
119 tools = filter(None, tools)
121 if SCons.Util.is_String(tool):
122 env.Tool(tool, toolpath)
126 # These names are controlled by SCons; users should never set or override
127 # them. This warning can optionally be turned off, but scons will still
128 # ignore the illegal variable names even if it's off.
129 reserved_construction_var_names = \
130 ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
132 def copy_non_reserved_keywords(dict):
133 result = our_deepcopy(dict)
134 for k in result.keys():
135 if k in reserved_construction_var_names:
136 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
137 "Ignoring attempt to set reserved variable `%s'" % k)
141 class BuilderWrapper:
142 """Wrapper class that associates an environment with a Builder at
144 def __init__(self, env, builder):
146 self.builder = builder
148 def __call__(self, *args, **kw):
149 return apply(self.builder, (self.env,) + args, kw)
151 # This allows a Builder to be executed directly
152 # through the Environment to which it's attached.
153 # In practice, we shouldn't need this, because
154 # builders actually get executed through a Node.
155 # But we do have a unit test for this, and can't
156 # yet rule out that it would be useful in the
157 # future, so leave it for now.
158 def execute(self, **kw):
160 apply(self.builder.execute, (), kw)
162 class BuilderDict(UserDict):
163 """This is a dictionary-like class used by an Environment to hold
164 the Builders. We need to do this because every time someone changes
165 the Builders in the Environment's BUILDERS dictionary, we must
166 update the Environment's attributes."""
167 def __init__(self, dict, env):
168 # Set self.env before calling the superclass initialization,
169 # because it will end up calling our other methods, which will
170 # need to point the values in this dictionary to self.env.
172 UserDict.__init__(self, dict)
174 def __setitem__(self, item, val):
175 UserDict.__setitem__(self, item, val)
177 self.setenvattr(item, val)
178 except AttributeError:
179 # Have to catch this because sometimes __setitem__ gets
180 # called out of __init__, when we don't have an env
181 # attribute yet, nor do we want one!
184 def setenvattr(self, item, val):
185 """Set the corresponding environment attribute for this Builder.
187 If the value is already a BuilderWrapper, we pull the builder
188 out of it and make another one, so that making a copy of an
189 existing BuilderDict is guaranteed separate wrappers for each
190 Builder + Environment pair."""
192 builder = val.builder
193 except AttributeError:
195 setattr(self.env, item, BuilderWrapper(self.env, builder))
197 def __delitem__(self, item):
198 UserDict.__delitem__(self, item)
199 delattr(self.env, item)
201 def update(self, dict):
202 for i, v in dict.items():
203 self.__setitem__(i, v)
206 """Base class for construction Environments. These are
207 the primary objects used to communicate dependency and
208 construction information to the build engine.
210 Keyword arguments supplied when the construction Environment
211 is created are construction variables used to initialize the
215 #######################################################################
216 # This is THE class for interacting with the SCons build engine,
217 # and it contains a lot of stuff, so we're going to try to keep this
218 # a little organized by grouping the methods.
219 #######################################################################
221 #######################################################################
222 # Methods that make an Environment act like a dictionary. These have
223 # the expected standard names for Python mapping objects. Note that
224 # we don't actually make an Environment a subclass of UserDict for
225 # performance reasons. Note also that we only supply methods for
226 # dictionary functionality that we actually need and use.
227 #######################################################################
235 if __debug__: logInstanceCreation(self)
236 self.fs = SCons.Node.FS.default_fs
237 self.ans = SCons.Node.Alias.default_ans
238 self.lookup_list = SCons.Node.arg2nodes_lookups
239 self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
241 self._dict['__env__'] = self
242 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
245 platform = self._dict.get('PLATFORM', None)
247 platform = SCons.Platform.Platform()
248 if SCons.Util.is_String(platform):
249 platform = SCons.Platform.Platform(platform)
250 self._dict['PLATFORM'] = str(platform)
253 # Apply the passed-in variables before calling the tools,
254 # because they may use some of them:
255 apply(self.Replace, (), kw)
257 # Update the environment with the customizable options
258 # before calling the tools, since they may use some of the options:
263 tools = self._dict.get('TOOLS', None)
266 apply_tools(self, tools, toolpath)
268 # Reapply the passed in variables after calling the tools,
269 # since they should overide anything set by the tools:
270 apply(self.Replace, (), kw)
272 # Update the environment with the customizable options
273 # after calling the tools, since they should override anything
278 def __cmp__(self, other):
279 # Since an Environment now has an '__env__' construction variable
280 # that refers to itself, delete that variable to avoid infinite
281 # loops when comparing the underlying dictionaries in some Python
282 # versions (*cough* 1.5.2 *cough*)...
283 sdict = self._dict.copy()
285 odict = other._dict.copy()
287 return cmp(sdict, odict)
289 def __getitem__(self, key):
290 return self._dict[key]
292 def __setitem__(self, key, value):
293 if key in reserved_construction_var_names:
294 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
295 "Ignoring attempt to set reserved variable `%s'" % key)
296 elif key == 'BUILDERS':
302 self._dict[key] = BuilderDict(kwbd, self)
303 self._dict[key].update(value)
304 elif key == 'SCANNERS':
305 self._dict[key] = value
306 self.scanner_map_delete()
308 if not SCons.Util.is_valid_construction_var(key):
309 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
310 self._dict[key] = value
312 def __delitem__(self, key):
316 "Emulates the items() method of dictionaries."""
317 return self._dict.items()
319 def has_key(self, key):
320 return self._dict.has_key(key)
322 def get(self, key, default=None):
323 "Emulates the get() method of dictionaries."""
324 return self._dict.get(key, default)
326 #######################################################################
327 # Utility methods that are primarily for internal use by SCons.
328 # These begin with lower-case letters. Note that the subst() method
329 # is actually already out of the closet and used by people.
330 #######################################################################
332 def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
333 if node_factory is _null:
334 node_factory = self.fs.File
335 if lookup_list is _null:
336 lookup_list = self.lookup_list
341 if SCons.Util.is_List(args):
342 args = SCons.Util.flatten(args)
348 if SCons.Util.is_String(v):
350 for l in lookup_list:
355 if SCons.Util.is_String(n):
356 n = self.subst(n, raw=1)
359 if SCons.Util.is_List(n):
364 v = node_factory(self.subst(v, raw=1))
365 if SCons.Util.is_List(v):
374 def get_calculator(self):
376 return self._calculator
377 except AttributeError:
379 module = self._calc_module
380 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
381 except AttributeError:
382 # Note that we're calling get_calculator() here, so the
383 # DefaultEnvironment() must have a _calc_module attribute
384 # to avoid infinite recursion.
385 c = SCons.Defaults.DefaultEnvironment().get_calculator()
389 def get_builder(self, name):
390 """Fetch the builder with the specified name from the environment.
393 return self._dict['BUILDERS'][name]
397 def get_scanner(self, skey):
398 """Find the appropriate scanner given a key (usually a file suffix).
401 sm = self.scanner_map
402 except AttributeError:
404 scanners = self._dict['SCANNERS']
406 self.scanner_map = {}
409 self.scanner_map = sm = {}
410 # Reverse the scanner list so that, if multiple scanners
411 # claim they can scan the same suffix, earlier scanners
412 # in the list will overwrite later scanners, so that
413 # the result looks like a "first match" to the user.
414 if not SCons.Util.is_List(scanners):
415 scanners = [scanners]
417 for scanner in scanners:
418 for k in scanner.get_skeys(self):
425 def scanner_map_delete(self, kw=None):
426 """Delete the cached scanner map (if we need to).
428 if not kw is None and not kw.has_key('SCANNERS'):
432 except AttributeError:
435 def subst(self, string, raw=0, target=None, source=None, dict=None, conv=None):
436 """Recursively interpolates construction variables from the
437 Environment into the specified string, returning the expanded
438 result. Construction variables are specified by a $ prefix
439 in the string and begin with an initial underscore or
440 alphabetic character followed by any number of underscores
441 or alphanumeric characters. The construction variable names
442 may be surrounded by curly braces to separate the name from
445 return SCons.Util.scons_subst(string, self, raw, target, source, dict, conv)
447 def subst_kw(self, kw, raw=0, target=None, source=None, dict=None):
449 for k, v in kw.items():
450 k = self.subst(k, raw, target, source, dict)
451 if SCons.Util.is_String(v):
452 v = self.subst(v, raw, target, source, dict)
456 def subst_list(self, string, raw=0, target=None, source=None, dict=None, conv=None):
457 """Calls through to SCons.Util.scons_subst_list(). See
458 the documentation for that function."""
459 return SCons.Util.scons_subst_list(string, self, raw, target, source, dict, conv)
462 def subst_path(self, path):
463 """Substitute a path list, turning EntryProxies into Nodes
464 and leaving Nodes (and other objects) as-is."""
466 if not SCons.Util.is_List(path):
470 """This is the "string conversion" routine that we have our
471 substitutions use to return Nodes, not strings. This relies
472 on the fact that an EntryProxy object has a get() method that
473 returns the underlying Node that it wraps, which is a bit of
474 architectural dependence that we might need to break or modify
475 in the future in response to additional requirements."""
478 except AttributeError:
486 if SCons.Util.is_String(p):
487 p = self.subst(p, conv=s)
488 if SCons.Util.is_List(p):
492 # We have an object plus a string, or multiple
493 # objects that we need to smush together. No choice
494 # but to make them into a string.
495 p = string.join(map(SCons.Util.to_String, p), '')
501 subst_target_source = subst
503 def _update(self, dict):
504 """Update an environment's values directly, bypassing the normal
505 checks that occur when users try to set items.
507 self._dict.update(dict)
509 def use_build_signature(self):
511 return self._build_signature
512 except AttributeError:
513 b = SCons.Defaults.DefaultEnvironment()._build_signature
514 self._build_signature = b
517 #######################################################################
518 # Public methods for manipulating an Environment. These begin with
519 # upper-case letters. The essential characteristic of methods in
520 # this section is that they do *not* have corresponding same-named
521 # global functions. For example, a stand-alone Append() function
522 # makes no sense, because Append() is all about appending values to
523 # an Environment's construction variables.
524 #######################################################################
526 def Append(self, **kw):
527 """Append values to existing construction variables
530 kw = copy_non_reserved_keywords(kw)
531 for key, val in kw.items():
532 # It would be easier on the eyes to write this using
533 # "continue" statements whenever we finish processing an item,
534 # but Python 1.5.2 apparently doesn't let you use "continue"
535 # within try:-except: blocks, so we have to nest our code.
537 orig = self._dict[key]
539 # No existing variable in the environment, so just set
540 # it to the new value.
541 self._dict[key] = val
544 # Most straightforward: just try to add them
545 # together. This will work in most cases, when the
546 # original and new values are of compatible types.
547 self._dict[key] = orig + val
550 # Try to update a dictionary value with another.
551 # If orig isn't a dictionary, it won't have an
552 # update() method; if val isn't a dictionary,
553 # it won't have a keys() method. Either way,
554 # it's an AttributeError.
556 except AttributeError:
558 # Check if the original is a list.
559 add_to_orig = orig.append
560 except AttributeError:
561 # The original isn't a list, but the new
562 # value is (by process of elimination),
563 # so insert the original in the new value
564 # (if there's one to insert) and replace
565 # the variable with it.
568 self._dict[key] = val
570 # The original is a list, so append the new
571 # value to it (if there's a value to append).
574 self.scanner_map_delete(kw)
576 def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
577 """Append path elements to the path 'name' in the 'ENV'
578 dictionary for this environment. Will only add any particular
579 path once, and will normpath and normcase all paths to help
580 assure this. This can also handle the case where the env
581 variable is a list instead of a string.
585 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
586 orig = self._dict[envname][name]
588 nv = SCons.Util.AppendPath(orig, newpath, sep)
590 if not self._dict.has_key(envname):
591 self._dict[envname] = {}
593 self._dict[envname][name] = nv
595 def AppendUnique(self, **kw):
596 """Append values to existing construction variables
597 in an Environment, if they're not already there.
599 kw = copy_non_reserved_keywords(kw)
600 for key, val in kw.items():
601 if not self._dict.has_key(key):
602 self._dict[key] = val
603 elif SCons.Util.is_Dict(self._dict[key]) and \
604 SCons.Util.is_Dict(val):
605 self._dict[key].update(val)
606 elif SCons.Util.is_List(val):
608 if not SCons.Util.is_List(dk):
610 val = filter(lambda x, dk=dk: x not in dk, val)
611 self._dict[key] = dk + val
614 if SCons.Util.is_List(dk):
616 self._dict[key] = dk + val
618 self._dict[key] = self._dict[key] + val
619 self.scanner_map_delete(kw)
621 def Copy(self, tools=None, toolpath=[], **kw):
622 """Return a copy of a construction Environment. The
623 copy is like a Python "deep copy"--that is, independent
624 copies are made recursively of each objects--except that
625 a reference is copied when an object is not deep-copyable
626 (like a function). There are no references to any mutable
627 objects in the original Environment.
629 clone = copy.copy(self)
630 clone._dict = our_deepcopy(self._dict)
631 clone['__env__'] = clone
633 cbd = clone._dict['BUILDERS']
634 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
638 apply_tools(clone, tools, toolpath)
640 # Apply passed-in variables after the new tools.
641 kw = copy_non_reserved_keywords(kw)
643 for key, value in kw.items():
644 new[key] = SCons.Util.scons_subst_once(value, self, key)
645 apply(clone.Replace, (), new)
648 def Detect(self, progs):
649 """Return the first available program in progs.
651 if not SCons.Util.is_List(progs):
654 path = self.WhereIs(prog)
658 def Dictionary(self, *args):
661 dlist = map(lambda x, s=self: s._dict[x], args)
666 def Dump(self, key = None):
668 Using the standard Python pretty printer, dump the contents of the
669 scons build environment to stdout.
671 If the key passed in is anything other than None, then that will
672 be used as an index into the build environment dictionary and
673 whatever is found there will be fed into the pretty printer. Note
674 that this key is case sensitive.
677 pp = pprint.PrettyPrinter(indent=2)
679 dict = self.Dictionary(key)
681 dict = self.Dictionary()
682 return pp.pformat(dict)
684 def FindIxes(self, paths, prefix, suffix):
686 Search a list of paths for something that matches the prefix and suffix.
688 paths - the list of paths or nodes.
689 prefix - construction variable for the prefix.
690 suffix - construction variable for the suffix.
693 suffix = self.subst('$'+suffix)
694 prefix = self.subst('$'+prefix)
697 dir,name = os.path.split(str(path))
698 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
701 def Override(self, overrides):
703 Produce a modified environment whose variables
704 are overriden by the overrides dictionaries.
706 overrides - a dictionary that will override
707 the variables of this environment.
709 This function is much more efficient than Copy()
710 or creating a new Environment because it doesn't do
711 a deep copy of the dictionary, and doesn't do a copy
712 at all if there are no overrides.
716 env = copy.copy(self)
717 env._dict = copy.copy(self._dict)
719 overrides = copy_non_reserved_keywords(overrides)
721 for key, value in overrides.items():
722 new[key] = SCons.Util.scons_subst_once(value, self, key)
723 env._dict.update(new)
728 def ParseConfig(self, command, function=None):
730 Use the specified function to parse the output of the command
731 in order to modify the current environment. The 'command' can
732 be a string or a list of strings representing a command and
733 it's arguments. 'Function' is an optional argument that takes
734 the environment and the output of the command. If no function is
735 specified, the output will be treated as the output of a typical
736 'X-config' command (i.e. gtk-config) and used to append to the
737 ASFLAGS, CCFLAGS, CPPFLAGS, CPPPATH, LIBPATH, LIBS, LINKFLAGS
738 and CCFLAGS variables.
741 # the default parse function
742 def parse_conf(env, output, fs=self.fs):
753 params = string.split(output)
754 append_next_arg_to='' # for multi-word args
756 if append_next_arg_to:
757 dict[append_next_arg_to].append(arg)
758 append_next_arg_to = ''
760 dict['LIBS'].append(fs.File(arg))
761 elif arg[:2] == '-L':
762 dict['LIBPATH'].append(arg[2:])
763 elif arg[:2] == '-l':
764 dict['LIBS'].append(arg[2:])
765 elif arg[:2] == '-I':
766 dict['CPPPATH'].append(arg[2:])
767 elif arg[:4] == '-Wa,':
768 dict['ASFLAGS'].append(arg)
769 elif arg[:4] == '-Wl,':
770 dict['LINKFLAGS'].append(arg)
771 elif arg[:4] == '-Wp,':
772 dict['CPPFLAGS'].append(arg)
773 elif arg == '-framework':
774 dict['LINKFLAGS'].append(arg)
775 append_next_arg_to='LINKFLAGS'
776 elif arg == '-mno-cygwin':
777 dict['CCFLAGS'].append(arg)
778 dict['LINKFLAGS'].append(arg)
779 elif arg == '-mwindows':
780 dict['LINKFLAGS'].append(arg)
781 elif arg == '-pthread':
782 dict['CCFLAGS'].append(arg)
783 dict['LINKFLAGS'].append(arg)
785 dict['CCFLAGS'].append(arg)
786 apply(env.Append, (), dict)
789 function = parse_conf
790 if type(command) is type([]):
791 command = string.join(command)
792 command = self.subst(command)
793 return function(self, os.popen(command).read())
795 def ParseDepends(self, filename, must_exist=None):
797 Parse a mkdep-style file for explicit dependencies. This is
798 completely abusable, and should be unnecessary in the "normal"
799 case of proper SCons configuration, but it may help make
800 the transition from a Make hierarchy easier for some people
801 to swallow. It can also be genuinely useful when using a tool
802 that can write a .d file, but for which writing a scanner would
806 fp = open(filename, 'r')
811 for line in SCons.Util.LogicalLines(fp).readlines():
815 target, depends = string.split(line, ':', 1)
819 self.Depends(string.split(target), string.split(depends))
821 def Platform(self, platform):
822 platform = self.subst(platform)
823 return SCons.Platform.Platform(platform)(self)
825 def Prepend(self, **kw):
826 """Prepend values to existing construction variables
829 kw = copy_non_reserved_keywords(kw)
830 for key, val in kw.items():
831 # It would be easier on the eyes to write this using
832 # "continue" statements whenever we finish processing an item,
833 # but Python 1.5.2 apparently doesn't let you use "continue"
834 # within try:-except: blocks, so we have to nest our code.
836 orig = self._dict[key]
838 # No existing variable in the environment, so just set
839 # it to the new value.
840 self._dict[key] = val
843 # Most straightforward: just try to add them
844 # together. This will work in most cases, when the
845 # original and new values are of compatible types.
846 self._dict[key] = val + orig
849 # Try to update a dictionary value with another.
850 # If orig isn't a dictionary, it won't have an
851 # update() method; if val isn't a dictionary,
852 # it won't have a keys() method. Either way,
853 # it's an AttributeError.
855 except AttributeError:
857 # Check if the added value is a list.
858 add_to_val = val.append
859 except AttributeError:
860 # The added value isn't a list, but the
861 # original is (by process of elimination),
862 # so insert the the new value in the original
863 # (if there's one to insert).
867 # The added value is a list, so append
868 # the original to it (if there's a value
872 self._dict[key] = val
873 self.scanner_map_delete(kw)
875 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
876 """Prepend path elements to the path 'name' in the 'ENV'
877 dictionary for this environment. Will only add any particular
878 path once, and will normpath and normcase all paths to help
879 assure this. This can also handle the case where the env
880 variable is a list instead of a string.
884 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
885 orig = self._dict[envname][name]
887 nv = SCons.Util.PrependPath(orig, newpath, sep)
889 if not self._dict.has_key(envname):
890 self._dict[envname] = {}
892 self._dict[envname][name] = nv
894 def PrependUnique(self, **kw):
895 """Append values to existing construction variables
896 in an Environment, if they're not already there.
898 kw = copy_non_reserved_keywords(kw)
899 for key, val in kw.items():
900 if not self._dict.has_key(key):
901 self._dict[key] = val
902 elif SCons.Util.is_Dict(self._dict[key]) and \
903 SCons.Util.is_Dict(val):
904 self._dict[key].update(val)
905 elif SCons.Util.is_List(val):
907 if not SCons.Util.is_List(dk):
909 val = filter(lambda x, dk=dk: x not in dk, val)
910 self._dict[key] = val + dk
913 if SCons.Util.is_List(dk):
915 self._dict[key] = val + dk
917 self._dict[key] = val + dk
918 self.scanner_map_delete(kw)
920 def Replace(self, **kw):
921 """Replace existing construction variables in an Environment
922 with new construction variables and/or values.
925 kwbd = our_deepcopy(kw['BUILDERS'])
927 self.__setitem__('BUILDERS', kwbd)
930 kw = copy_non_reserved_keywords(kw)
931 self._dict.update(our_deepcopy(kw))
932 self.scanner_map_delete(kw)
934 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
936 Replace old_prefix with new_prefix and old_suffix with new_suffix.
938 env - Environment used to interpolate variables.
939 path - the path that will be modified.
940 old_prefix - construction variable for the old prefix.
941 old_suffix - construction variable for the old suffix.
942 new_prefix - construction variable for the new prefix.
943 new_suffix - construction variable for the new suffix.
945 old_prefix = self.subst('$'+old_prefix)
946 old_suffix = self.subst('$'+old_suffix)
948 new_prefix = self.subst('$'+new_prefix)
949 new_suffix = self.subst('$'+new_suffix)
951 dir,name = os.path.split(str(path))
952 if name[:len(old_prefix)] == old_prefix:
953 name = name[len(old_prefix):]
954 if name[-len(old_suffix):] == old_suffix:
955 name = name[:-len(old_suffix)]
956 return os.path.join(dir, new_prefix+name+new_suffix)
958 def SetDefault(self, **kw):
960 if self._dict.has_key(k):
962 apply(self.Replace, (), kw)
964 def Tool(self, tool, toolpath=[]):
965 tool = self.subst(tool)
966 return SCons.Tool.Tool(tool, map(self.subst, toolpath))(self)
968 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
969 """Find prog in the path.
973 path = self['ENV']['PATH']
976 elif SCons.Util.is_String(path):
977 path = self.subst(path)
980 pathext = self['ENV']['PATHEXT']
983 elif SCons.Util.is_String(pathext):
984 pathext = self.subst(pathext)
985 path = SCons.Util.WhereIs(prog, path, pathext, reject)
989 #######################################################################
990 # Public methods for doing real "SCons stuff" (manipulating
991 # dependencies, setting attributes on targets, etc.). These begin
992 # with upper-case letters. The essential characteristic of methods
993 # in this section is that they all *should* have corresponding
994 # same-named global functions.
995 #######################################################################
997 def Action(self, *args, **kw):
998 nargs = self.subst(args)
999 nkw = self.subst_kw(kw)
1000 return apply(SCons.Action.Action, nargs, nkw)
1002 def AddPreAction(self, files, action):
1003 nodes = self.arg2nodes(files, self.fs.Entry)
1004 action = SCons.Action.Action(action)
1006 n.add_pre_action(action)
1009 def AddPostAction(self, files, action):
1010 nodes = self.arg2nodes(files, self.fs.Entry)
1011 action = SCons.Action.Action(action)
1013 n.add_post_action(action)
1016 def Alias(self, target, *source, **kw):
1017 if not SCons.Util.is_List(target):
1021 if not isinstance(t, SCons.Node.Alias.Alias):
1022 t = self.arg2nodes(self.subst(t), self.ans.Alias)[0]
1032 if not SCons.Util.is_List(s):
1035 s = self.arg2nodes(s, self.fs.Entry)
1037 AliasBuilder(self, t, s)
1040 def AlwaysBuild(self, *targets):
1043 tlist.extend(self.arg2nodes(t, self.fs.File))
1045 t.set_always_build()
1048 def BuildDir(self, build_dir, src_dir, duplicate=1):
1049 build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
1050 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1051 self.fs.BuildDir(build_dir, src_dir, duplicate)
1053 def Builder(self, **kw):
1054 nkw = self.subst_kw(kw)
1055 return apply(SCons.Builder.Builder, [], nkw)
1057 def CacheDir(self, path):
1058 self.fs.CacheDir(self.subst(path))
1060 def Clean(self, targets, files):
1062 tlist = self.arg2nodes(targets, self.fs.Entry)
1063 flist = self.arg2nodes(files, self.fs.Entry)
1066 CleanTargets[t].extend(flist)
1068 CleanTargets[t] = flist
1070 def Configure(self, *args, **kw):
1073 nargs = nargs + self.subst_list(args)[0]
1074 nkw = self.subst_kw(kw)
1075 nkw['called_from_env_method'] = 1
1077 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1080 return apply(SCons.SConf.SConf, nargs, nkw)
1082 def Command(self, target, source, action, **kw):
1083 """Builds the supplied target files from the supplied
1084 source files using the supplied action. Action may
1085 be any type that the Builder constructor will accept
1087 nkw = self.subst_kw(kw)
1088 nkw['action'] = action
1089 nkw['source_factory'] = self.fs.Entry
1090 bld = apply(SCons.Builder.Builder, (), nkw)
1091 return bld(self, target, source)
1093 def Depends(self, target, dependency):
1094 """Explicity specify that 'target's depend on 'dependency'."""
1095 tlist = self.arg2nodes(target, self.fs.Entry)
1096 dlist = self.arg2nodes(dependency, self.fs.Entry)
1098 t.add_dependency(dlist)
1101 def Dir(self, name, *args, **kw):
1104 return apply(self.fs.Dir, (self.subst(name),) + args, kw)
1106 def Environment(self, **kw):
1107 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1109 def Execute(self, action, *args, **kw):
1110 """Directly execute an action through an Environment
1112 action = apply(self.Action, (action,) + args, kw)
1113 return action([], [], self)
1115 def File(self, name, *args, **kw):
1118 return apply(self.fs.File, (self.subst(name),) + args, kw)
1120 def FindFile(self, file, dirs):
1121 file = self.subst(file)
1122 nodes = self.arg2nodes(dirs, self.fs.Dir)
1123 return SCons.Node.FS.find_file(file, nodes, self.fs.File)
1125 def Flatten(self, sequence):
1126 return SCons.Util.flatten(sequence)
1128 def GetBuildPath(self, files):
1129 result = map(str, self.arg2nodes(files, self.fs.Entry))
1130 if SCons.Util.is_List(files):
1135 def Ignore(self, target, dependency):
1136 """Ignore a dependency."""
1137 tlist = self.arg2nodes(target, self.fs.Entry)
1138 dlist = self.arg2nodes(dependency, self.fs.Entry)
1143 def Install(self, dir, source):
1144 """Install specified files in the given directory."""
1146 dnodes = self.arg2nodes(dir, self.fs.Dir)
1148 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)
1150 sources = self.arg2nodes(source, self.fs.File)
1152 if SCons.Util.is_List(source):
1153 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))
1155 raise SCons.Errors.UserError, "Source `%s' of Install() is not a file. Install() source must be one or more files." % str(source)
1157 for dnode in dnodes:
1159 target = self.fs.File(src.name, dnode)
1160 tgt.extend(InstallBuilder(self, target, src))
1163 def InstallAs(self, target, source):
1164 """Install sources as targets."""
1165 sources = self.arg2nodes(source, self.fs.File)
1166 targets = self.arg2nodes(target, self.fs.File)
1168 for src, tgt in map(lambda x, y: (x, y), sources, targets):
1169 result.extend(InstallBuilder(self, tgt, src))
1172 def Literal(self, string):
1173 return SCons.Util.Literal(string)
1175 def Local(self, *targets):
1177 for targ in targets:
1178 if isinstance(targ, SCons.Node.Node):
1182 for t in self.arg2nodes(targ, self.fs.Entry):
1187 def Precious(self, *targets):
1190 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1195 def Repository(self, *dirs, **kw):
1196 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1197 apply(self.fs.Repository, dirs, kw)
1199 def Scanner(self, *args, **kw):
1202 if SCons.Util.is_String(arg):
1203 arg = self.subst(arg)
1205 nkw = self.subst_kw(kw)
1206 return apply(SCons.Scanner.Scanner, nargs, nkw)
1208 def SConsignFile(self, name=".sconsign", dbm_module=None):
1209 name = self.subst(name)
1210 if not os.path.isabs(name):
1211 name = os.path.join(str(self.fs.SConstruct_dir), name)
1212 SCons.SConsign.File(name, dbm_module)
1214 def SideEffect(self, side_effect, target):
1215 """Tell scons that side_effects are built as side
1216 effects of building targets."""
1217 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1218 targets = self.arg2nodes(target, self.fs.Entry)
1220 for side_effect in side_effects:
1221 if side_effect.multiple_side_effect_has_builder():
1222 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1223 side_effect.add_source(targets)
1224 side_effect.side_effect = 1
1225 self.Precious(side_effect)
1226 for target in targets:
1227 target.side_effects.append(side_effect)
1230 def SourceCode(self, entry, builder):
1231 """Arrange for a source code builder for (part of) a tree."""
1232 entries = self.arg2nodes(entry, self.fs.Entry)
1233 for entry in entries:
1234 entry.set_src_builder(builder)
1237 def SourceSignatures(self, type):
1238 type = self.subst(type)
1240 import SCons.Sig.MD5
1241 self._calc_module = SCons.Sig.MD5
1242 elif type == 'timestamp':
1243 import SCons.Sig.TimeStamp
1244 self._calc_module = SCons.Sig.TimeStamp
1246 raise UserError, "Unknown source signature type '%s'"%type
1248 def Split(self, arg):
1249 """This function converts a string or list into a list of strings
1250 or Nodes. This makes things easier for users by allowing files to
1251 be specified as a white-space separated list to be split.
1252 The input rules are:
1253 - A single string containing names separated by spaces. These will be
1254 split apart at the spaces.
1255 - A single Node instance
1256 - A list containing either strings or Node instances. Any strings
1257 in the list are not split at spaces.
1258 In all cases, the function returns a list of Nodes and strings."""
1259 if SCons.Util.is_List(arg):
1260 return map(self.subst, arg)
1261 elif SCons.Util.is_String(arg):
1262 return string.split(self.subst(arg))
1264 return [self.subst(arg)]
1266 def TargetSignatures(self, type):
1267 type = self.subst(type)
1269 self._build_signature = 1
1270 elif type == 'content':
1271 self._build_signature = 0
1273 raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1275 def Value(self, value):
1278 return SCons.Node.Python.Value(value)
1280 # The entry point that will be used by the external world
1281 # to refer to a construction environment. This allows the wrapper
1282 # interface to extend a construction environment for its own purposes
1283 # by subclassing SCons.Environment.Base and then assigning the
1284 # class to SCons.Environment.Environment.
1288 # An entry point for returning a proxy subclass instance that overrides
1289 # the subst*() methods so they don't actually perform construction
1290 # variable substitution. This is specifically intended to be the shim
1291 # layer in between global function calls (which don't want construction
1292 # variable substitution) and the DefaultEnvironment() (which would
1293 # substitute variables if left to its own devices)."""
1295 # We have to wrap this in a function that allows us to delay definition of
1296 # the class until it's necessary, so that when it subclasses Environment
1297 # it will pick up whatever Environment subclass the wrapper interface
1298 # might have assigned to SCons.Environment.Environment.
1300 def NoSubstitutionProxy(subject):
1301 class _NoSubstitutionProxy(Environment):
1302 def __init__(self, subject):
1303 self.__dict__['__subject'] = subject
1304 def __getattr__(self, name):
1305 return getattr(self.__dict__['__subject'], name)
1306 def __setattr__(self, name, value):
1307 return setattr(self.__dict__['__subject'], name, value)
1308 def raw_to_mode(self, dict):
1316 def subst(self, string, *args, **kwargs):
1318 def subst_kw(self, kw, *args, **kwargs):
1320 def subst_list(self, string, *args, **kwargs):
1321 nargs = (string, self,) + args
1324 self.raw_to_mode(nkw)
1325 return apply(SCons.Util.scons_subst_list, nargs, nkw)
1326 def subst_target_source(self, string, *args, **kwargs):
1327 nargs = (string, self,) + args
1330 self.raw_to_mode(nkw)
1331 return apply(SCons.Util.scons_subst, nargs, nkw)
1332 return _NoSubstitutionProxy(subject)