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.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):
120 # Store the toolpath in the Environment.
121 if toolpath is not None:
122 env['toolpath'] = toolpath
126 # Filter out null tools from the list.
127 for tool in filter(None, tools):
128 if SCons.Util.is_List(tool) or type(tool)==type(()):
130 toolargs = tool[1] # should be a dict of kw args
131 tool = apply(env.Tool, [toolname], toolargs)
135 # These names are controlled by SCons; users should never set or override
136 # them. This warning can optionally be turned off, but scons will still
137 # ignore the illegal variable names even if it's off.
138 reserved_construction_var_names = \
139 ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
141 def copy_non_reserved_keywords(dict):
142 result = our_deepcopy(dict)
143 for k in result.keys():
144 if k in reserved_construction_var_names:
145 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
146 "Ignoring attempt to set reserved variable `%s'" % k)
150 class BuilderWrapper:
151 """Wrapper class that associates an environment with a Builder at
153 def __init__(self, env, builder):
155 self.builder = builder
157 def __call__(self, target=None, source=_null, *args, **kw):
161 if not target is None and not SCons.Util.is_List(target):
163 if not source is None and not SCons.Util.is_List(source):
165 return apply(self.builder, (self.env, target, source) + args, kw)
167 # This allows a Builder to be executed directly
168 # through the Environment to which it's attached.
169 # In practice, we shouldn't need this, because
170 # builders actually get executed through a Node.
171 # But we do have a unit test for this, and can't
172 # yet rule out that it would be useful in the
173 # future, so leave it for now.
174 def execute(self, **kw):
176 apply(self.builder.execute, (), kw)
178 class BuilderDict(UserDict):
179 """This is a dictionary-like class used by an Environment to hold
180 the Builders. We need to do this because every time someone changes
181 the Builders in the Environment's BUILDERS dictionary, we must
182 update the Environment's attributes."""
183 def __init__(self, dict, env):
184 # Set self.env before calling the superclass initialization,
185 # because it will end up calling our other methods, which will
186 # need to point the values in this dictionary to self.env.
188 UserDict.__init__(self, dict)
190 def __setitem__(self, item, val):
191 UserDict.__setitem__(self, item, val)
193 self.setenvattr(item, val)
194 except AttributeError:
195 # Have to catch this because sometimes __setitem__ gets
196 # called out of __init__, when we don't have an env
197 # attribute yet, nor do we want one!
200 def setenvattr(self, item, val):
201 """Set the corresponding environment attribute for this Builder.
203 If the value is already a BuilderWrapper, we pull the builder
204 out of it and make another one, so that making a copy of an
205 existing BuilderDict is guaranteed separate wrappers for each
206 Builder + Environment pair."""
208 builder = val.builder
209 except AttributeError:
211 setattr(self.env, item, BuilderWrapper(self.env, builder))
213 def __delitem__(self, item):
214 UserDict.__delitem__(self, item)
215 delattr(self.env, item)
217 def update(self, dict):
218 for i, v in dict.items():
219 self.__setitem__(i, v)
221 class SubstitutionEnvironment:
222 """Base class for different flavors of construction environments.
224 This class contains a minimal set of methods that handle contruction
225 variable expansion and conversion of strings to Nodes, which may or
226 may not be actually useful as a stand-alone class. Which methods
227 ended up in this class is pretty arbitrary right now. They're
228 basically the ones which we've empirically determined are common to
229 the different construction environment subclasses, and most of the
230 others that use or touch the underlying dictionary of construction
233 Eventually, this class should contain all the methods that we
234 determine are necessary for a "minimal" interface to the build engine.
235 A full "native Python" SCons environment has gotten pretty heavyweight
236 with all of the methods and Tools and construction variables we've
237 jammed in there, so it would be nice to have a lighter weight
238 alternative for interfaces that don't need all of the bells and
239 whistles. (At some point, we'll also probably rename this class
240 "Base," since that more reflects what we want this class to become,
241 but because we've released comments that tell people to subclass
242 Environment.Base to create their own flavors of construction
243 environment, we'll save that for a future refactoring when this
244 class actually becomes useful.)
247 __metaclass__ = SCons.Memoize.Memoized_Metaclass
249 def __init__(self, **kw):
250 """Initialization of an underlying SubstitutionEnvironment class.
252 if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
253 self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS()
254 self.ans = SCons.Node.Alias.default_ans
255 self.lookup_list = SCons.Node.arg2nodes_lookups
256 self._dict = kw.copy()
258 def __cmp__(self, other):
259 return cmp(self._dict, other._dict)
261 def __delitem__(self, key):
265 def __getitem__(self, key):
266 return self._dict[key]
268 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 return self._dict.items()
299 def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
300 if node_factory is _null:
301 node_factory = self.fs.File
302 if lookup_list is _null:
303 lookup_list = self.lookup_list
308 if SCons.Util.is_List(args):
309 args = SCons.Util.flatten(args)
315 if SCons.Util.is_String(v):
317 for l in lookup_list:
322 if SCons.Util.is_String(n):
323 n = self.subst(n, raw=1)
326 if SCons.Util.is_List(n):
331 v = node_factory(self.subst(v, raw=1))
332 if SCons.Util.is_List(v):
347 def subst(self, string, raw=0, target=None, source=None, conv=None):
348 """Recursively interpolates construction variables from the
349 Environment into the specified string, returning the expanded
350 result. Construction variables are specified by a $ prefix
351 in the string and begin with an initial underscore or
352 alphabetic character followed by any number of underscores
353 or alphanumeric characters. The construction variable names
354 may be surrounded by curly braces to separate the name from
359 lvars['__env__'] = self
360 return SCons.Util.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
362 def subst_kw(self, kw, raw=0, target=None, source=None):
364 for k, v in kw.items():
365 k = self.subst(k, raw, target, source)
366 if SCons.Util.is_String(v):
367 v = self.subst(v, raw, target, source)
371 def subst_list(self, string, raw=0, target=None, source=None, conv=None):
372 """Calls through to SCons.Util.scons_subst_list(). See
373 the documentation for that function."""
376 lvars['__env__'] = self
377 return SCons.Util.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
379 def subst_path(self, path, target=None, source=None):
380 """Substitute a path list, turning EntryProxies into Nodes
381 and leaving Nodes (and other objects) as-is."""
383 if not SCons.Util.is_List(path):
387 """This is the "string conversion" routine that we have our
388 substitutions use to return Nodes, not strings. This relies
389 on the fact that an EntryProxy object has a get() method that
390 returns the underlying Node that it wraps, which is a bit of
391 architectural dependence that we might need to break or modify
392 in the future in response to additional requirements."""
395 except AttributeError:
403 if SCons.Util.is_String(p):
404 p = self.subst(p, target=target, source=source, conv=s)
405 if SCons.Util.is_List(p):
409 # We have an object plus a string, or multiple
410 # objects that we need to smush together. No choice
411 # but to make them into a string.
412 p = string.join(map(SCons.Util.to_String, p), '')
418 subst_target_source = subst
420 def Override(self, overrides):
422 Produce a modified environment whose variables are overriden by
423 the overrides dictionaries. "overrides" is a dictionary that
424 will override the variables of this environment.
426 This function is much more efficient than Copy() or creating
427 a new Environment because it doesn't copy the construction
428 environment dictionary, it just wraps the underlying construction
429 environment, and doesn't even create a wrapper object if there
433 o = copy_non_reserved_keywords(overrides)
435 for key, value in o.items():
436 overrides[key] = SCons.Util.scons_subst_once(value, self, key)
438 env = OverrideEnvironment(self, overrides)
443 class Base(SubstitutionEnvironment):
444 """Base class for "real" construction Environments. These are the
445 primary objects used to communicate dependency and construction
446 information to the build engine.
448 Keyword arguments supplied when the construction Environment
449 is created are construction variables used to initialize the
453 __metaclass__ = SCons.Memoize.Memoized_Metaclass
455 #######################################################################
456 # This is THE class for interacting with the SCons build engine,
457 # and it contains a lot of stuff, so we're going to try to keep this
458 # a little organized by grouping the methods.
459 #######################################################################
461 #######################################################################
462 # Methods that make an Environment act like a dictionary. These have
463 # the expected standard names for Python mapping objects. Note that
464 # we don't actually make an Environment a subclass of UserDict for
465 # performance reasons. Note also that we only supply methods for
466 # dictionary functionality that we actually need and use.
467 #######################################################################
476 Initialization of a basic SCons construction environment,
477 including setting up special construction variables like BUILDER,
478 PLATFORM, etc., and searching for and applying available Tools.
480 Note that we do *not* call the underlying base class
481 (SubsitutionEnvironment) initialization, because we need to
482 initialize things in a very specific order that doesn't work
483 with the much simpler base class initialization.
485 if __debug__: logInstanceCreation(self, 'Environment.Base')
486 self.fs = SCons.Node.FS.default_fs or SCons.Node.FS.FS()
487 self.ans = SCons.Node.Alias.default_ans
488 self.lookup_list = SCons.Node.arg2nodes_lookups
489 self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment)
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_builder(self, name):
533 """Fetch the builder with the specified name from the environment.
536 return self._dict['BUILDERS'][name]
540 def get_calculator(self):
543 module = self._calc_module
544 c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs)
545 except AttributeError:
546 # Note that we're calling get_calculator() here, so the
547 # DefaultEnvironment() must have a _calc_module attribute
548 # to avoid infinite recursion.
549 c = SCons.Defaults.DefaultEnvironment().get_calculator()
552 def get_factory(self, factory, default='File'):
553 """Return a factory function for creating Nodes for this
554 construction environment.
559 is_node = issubclass(factory, SCons.Node.Node)
561 # The specified factory isn't a Node itself--it's
562 # most likely None, or possibly a callable.
566 # The specified factory is a Node (sub)class. Try to
567 # return the FS method that corresponds to the Node's
568 # name--that is, we return self.fs.Dir if they want a Dir,
569 # self.fs.File for a File, etc.
570 try: name = factory.__name__
571 except AttributeError: pass
574 # They passed us None, or we picked up a name from a specified
575 # class, so return the FS method. (Note that we *don't*
576 # use our own self.{Dir,File} methods because that would
577 # cause env.subst() to be called twice on the file name,
578 # interfering with files that have $$ in them.)
579 factory = getattr(self.fs, name)
585 scanners = self._dict['SCANNERS']
590 # Reverse the scanner list so that, if multiple scanners
591 # claim they can scan the same suffix, earlier scanners
592 # in the list will overwrite later scanners, so that
593 # the result looks like a "first match" to the user.
594 if not SCons.Util.is_List(scanners):
595 scanners = [scanners]
597 scanners = scanners[:] # copy so reverse() doesn't mod original
599 for scanner in scanners:
600 for k in scanner.get_skeys(self):
604 def get_scanner(self, skey):
605 """Find the appropriate scanner given a key (usually a file suffix).
609 except (KeyError, TypeError): return None
615 def scanner_map_delete(self, kw=None):
616 """Delete the cached scanner map (if we need to).
618 if not kw is None and not kw.has_key('SCANNERS'):
622 def _update(self, dict):
623 """Update an environment's values directly, bypassing the normal
624 checks that occur when users try to set items.
627 self._dict.update(dict)
629 def use_build_signature(self):
631 return self._build_signature
632 except AttributeError:
633 b = SCons.Defaults.DefaultEnvironment()._build_signature
634 self._build_signature = b
637 #######################################################################
638 # Public methods for manipulating an Environment. These begin with
639 # upper-case letters. The essential characteristic of methods in
640 # this section is that they do *not* have corresponding same-named
641 # global functions. For example, a stand-alone Append() function
642 # makes no sense, because Append() is all about appending values to
643 # an Environment's construction variables.
644 #######################################################################
646 def Append(self, **kw):
647 """Append values to existing construction variables
650 kw = copy_non_reserved_keywords(kw)
651 for key, val in kw.items():
652 # It would be easier on the eyes to write this using
653 # "continue" statements whenever we finish processing an item,
654 # but Python 1.5.2 apparently doesn't let you use "continue"
655 # within try:-except: blocks, so we have to nest our code.
657 orig = self._dict[key]
659 # No existing variable in the environment, so just set
660 # it to the new value.
661 self._dict[key] = val
664 # Most straightforward: just try to add them
665 # together. This will work in most cases, when the
666 # original and new values are of compatible types.
667 self._dict[key] = orig + val
670 # Try to update a dictionary value with another.
671 # If orig isn't a dictionary, it won't have an
672 # update() method; if val isn't a dictionary,
673 # it won't have a keys() method. Either way,
674 # it's an AttributeError.
676 except AttributeError:
678 # Check if the original is a list.
679 add_to_orig = orig.append
680 except AttributeError:
681 # The original isn't a list, but the new
682 # value is (by process of elimination),
683 # so insert the original in the new value
684 # (if there's one to insert) and replace
685 # the variable with it.
688 self._dict[key] = val
690 # The original is a list, so append the new
691 # value to it (if there's a value to append).
694 self.scanner_map_delete(kw)
696 def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
697 """Append path elements to the path 'name' in the 'ENV'
698 dictionary for this environment. Will only add any particular
699 path once, and will normpath and normcase all paths to help
700 assure this. This can also handle the case where the env
701 variable is a list instead of a string.
705 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
706 orig = self._dict[envname][name]
708 nv = SCons.Util.AppendPath(orig, newpath, sep)
710 if not self._dict.has_key(envname):
711 self._dict[envname] = {}
713 self._dict[envname][name] = nv
715 def AppendUnique(self, **kw):
716 """Append values to existing construction variables
717 in an Environment, if they're not already there.
719 kw = copy_non_reserved_keywords(kw)
720 for key, val in kw.items():
721 if not self._dict.has_key(key) or not self._dict[key]:
722 self._dict[key] = val
723 elif SCons.Util.is_Dict(self._dict[key]) and \
724 SCons.Util.is_Dict(val):
725 self._dict[key].update(val)
726 elif SCons.Util.is_List(val):
728 if not SCons.Util.is_List(dk):
730 val = filter(lambda x, dk=dk: x not in dk, val)
731 self._dict[key] = dk + val
734 if SCons.Util.is_List(dk):
736 self._dict[key] = dk + val
738 self._dict[key] = self._dict[key] + val
739 self.scanner_map_delete(kw)
741 def Copy(self, tools=[], toolpath=None, **kw):
742 """Return a copy of a construction Environment. The
743 copy is like a Python "deep copy"--that is, independent
744 copies are made recursively of each objects--except that
745 a reference is copied when an object is not deep-copyable
746 (like a function). There are no references to any mutable
747 objects in the original Environment.
749 clone = copy.copy(self)
750 clone._dict = our_deepcopy(self._dict)
752 cbd = clone._dict['BUILDERS']
753 clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
757 apply_tools(clone, tools, toolpath)
759 # Apply passed-in variables after the new tools.
760 kw = copy_non_reserved_keywords(kw)
762 for key, value in kw.items():
763 new[key] = SCons.Util.scons_subst_once(value, self, key)
764 apply(clone.Replace, (), new)
765 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentCopy')
768 def Detect(self, progs):
769 """Return the first available program in progs. __cacheable__
771 if not SCons.Util.is_List(progs):
774 path = self.WhereIs(prog)
778 def Dictionary(self, *args):
781 dlist = map(lambda x, s=self: s._dict[x], args)
786 def Dump(self, key = None):
788 Using the standard Python pretty printer, dump the contents of the
789 scons build environment to stdout.
791 If the key passed in is anything other than None, then that will
792 be used as an index into the build environment dictionary and
793 whatever is found there will be fed into the pretty printer. Note
794 that this key is case sensitive.
797 pp = pprint.PrettyPrinter(indent=2)
799 dict = self.Dictionary(key)
801 dict = self.Dictionary()
802 return pp.pformat(dict)
804 def FindIxes(self, paths, prefix, suffix):
806 Search a list of paths for something that matches the prefix and suffix.
808 paths - the list of paths or nodes.
809 prefix - construction variable for the prefix.
810 suffix - construction variable for the suffix.
813 suffix = self.subst('$'+suffix)
814 prefix = self.subst('$'+prefix)
817 dir,name = os.path.split(str(path))
818 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
821 def ParseConfig(self, command, function=None, unique=1):
823 Use the specified function to parse the output of the command
824 in order to modify the current environment. The 'command' can
825 be a string or a list of strings representing a command and
826 it's arguments. 'Function' is an optional argument that takes
827 the environment and the output of the command. If no function is
828 specified, the output will be treated as the output of a typical
829 'X-config' command (i.e. gtk-config) and used to append to the
830 ASFLAGS, CCFLAGS, CPPFLAGS, CPPPATH, LIBPATH, LIBS, LINKFLAGS
831 and CCFLAGS variables.
834 # the default parse function
835 def parse_conf(env, output, fs=self.fs, unique=unique):
846 params = string.split(output)
847 append_next_arg_to='' # for multi-word args
849 if append_next_arg_to:
850 dict[append_next_arg_to].append(arg)
851 append_next_arg_to = ''
853 dict['LIBS'].append(fs.File(arg))
854 elif arg[:2] == '-L':
856 dict['LIBPATH'].append(arg[2:])
858 append_next_arg_to = 'LIBPATH'
859 elif arg[:2] == '-l':
861 dict['LIBS'].append(arg[2:])
863 append_next_arg_to = 'LIBS'
864 elif arg[:2] == '-I':
866 dict['CPPPATH'].append(arg[2:])
868 append_next_arg_to = 'CPPPATH'
869 elif arg[:4] == '-Wa,':
870 dict['ASFLAGS'].append(arg)
871 elif arg[:4] == '-Wl,':
872 dict['LINKFLAGS'].append(arg)
873 elif arg[:4] == '-Wp,':
874 dict['CPPFLAGS'].append(arg)
875 elif arg == '-framework':
876 dict['LINKFLAGS'].append(arg)
877 append_next_arg_to='LINKFLAGS'
878 elif arg == '-mno-cygwin':
879 dict['CCFLAGS'].append(arg)
880 dict['LINKFLAGS'].append(arg)
881 elif arg == '-mwindows':
882 dict['LINKFLAGS'].append(arg)
883 elif arg == '-pthread':
884 dict['CCFLAGS'].append(arg)
885 dict['LINKFLAGS'].append(arg)
887 dict['CCFLAGS'].append(arg)
889 appender = env.AppendUnique
891 appender = env.Append
892 apply(appender, (), dict)
895 function = parse_conf
896 if type(command) is type([]):
897 command = string.join(command)
898 command = self.subst(command)
899 return function(self, os.popen(command).read())
901 def ParseDepends(self, filename, must_exist=None, only_one=0):
903 Parse a mkdep-style file for explicit dependencies. This is
904 completely abusable, and should be unnecessary in the "normal"
905 case of proper SCons configuration, but it may help make
906 the transition from a Make hierarchy easier for some people
907 to swallow. It can also be genuinely useful when using a tool
908 that can write a .d file, but for which writing a scanner would
911 filename = self.subst(filename)
913 fp = open(filename, 'r')
918 lines = SCons.Util.LogicalLines(fp).readlines()
919 lines = filter(lambda l: l[0] != '#', lines)
923 target, depends = string.split(line, ':', 1)
924 except (AttributeError, TypeError, ValueError):
925 # Python 1.5.2 throws TypeError if line isn't a string,
926 # Python 2.x throws AttributeError because it tries
927 # to call line.split(). Either can throw ValueError
928 # if the line doesn't split into two or more elements.
931 tdlist.append((string.split(target), string.split(depends)))
933 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
935 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets)
936 for target, depends in tdlist:
937 self.Depends(target, depends)
939 def Platform(self, platform):
940 platform = self.subst(platform)
941 return SCons.Platform.Platform(platform)(self)
943 def Prepend(self, **kw):
944 """Prepend values to existing construction variables
947 kw = copy_non_reserved_keywords(kw)
948 for key, val in kw.items():
949 # It would be easier on the eyes to write this using
950 # "continue" statements whenever we finish processing an item,
951 # but Python 1.5.2 apparently doesn't let you use "continue"
952 # within try:-except: blocks, so we have to nest our code.
954 orig = self._dict[key]
956 # No existing variable in the environment, so just set
957 # it to the new value.
958 self._dict[key] = val
961 # Most straightforward: just try to add them
962 # together. This will work in most cases, when the
963 # original and new values are of compatible types.
964 self._dict[key] = val + orig
967 # Try to update a dictionary value with another.
968 # If orig isn't a dictionary, it won't have an
969 # update() method; if val isn't a dictionary,
970 # it won't have a keys() method. Either way,
971 # it's an AttributeError.
973 except AttributeError:
975 # Check if the added value is a list.
976 add_to_val = val.append
977 except AttributeError:
978 # The added value isn't a list, but the
979 # original is (by process of elimination),
980 # so insert the the new value in the original
981 # (if there's one to insert).
985 # The added value is a list, so append
986 # the original to it (if there's a value
990 self._dict[key] = val
991 self.scanner_map_delete(kw)
993 def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
994 """Prepend path elements to the path 'name' in the 'ENV'
995 dictionary for this environment. Will only add any particular
996 path once, and will normpath and normcase all paths to help
997 assure this. This can also handle the case where the env
998 variable is a list instead of a string.
1002 if self._dict.has_key(envname) and self._dict[envname].has_key(name):
1003 orig = self._dict[envname][name]
1005 nv = SCons.Util.PrependPath(orig, newpath, sep)
1007 if not self._dict.has_key(envname):
1008 self._dict[envname] = {}
1010 self._dict[envname][name] = nv
1012 def PrependUnique(self, **kw):
1013 """Append values to existing construction variables
1014 in an Environment, if they're not already there.
1016 kw = copy_non_reserved_keywords(kw)
1017 for key, val in kw.items():
1018 if not self._dict.has_key(key) or not self._dict[key]:
1019 self._dict[key] = val
1020 elif SCons.Util.is_Dict(self._dict[key]) and \
1021 SCons.Util.is_Dict(val):
1022 self._dict[key].update(val)
1023 elif SCons.Util.is_List(val):
1024 dk = self._dict[key]
1025 if not SCons.Util.is_List(dk):
1027 val = filter(lambda x, dk=dk: x not in dk, val)
1028 self._dict[key] = val + dk
1030 dk = self._dict[key]
1031 if SCons.Util.is_List(dk):
1033 self._dict[key] = val + dk
1035 self._dict[key] = val + dk
1036 self.scanner_map_delete(kw)
1038 def Replace(self, **kw):
1039 """Replace existing construction variables in an Environment
1040 with new construction variables and/or values.
1043 kwbd = our_deepcopy(kw['BUILDERS'])
1045 self.__setitem__('BUILDERS', kwbd)
1048 kw = copy_non_reserved_keywords(kw)
1049 self._update(our_deepcopy(kw))
1050 self.scanner_map_delete(kw)
1052 def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1054 Replace old_prefix with new_prefix and old_suffix with new_suffix.
1056 env - Environment used to interpolate variables.
1057 path - the path that will be modified.
1058 old_prefix - construction variable for the old prefix.
1059 old_suffix - construction variable for the old suffix.
1060 new_prefix - construction variable for the new prefix.
1061 new_suffix - construction variable for the new suffix.
1063 old_prefix = self.subst('$'+old_prefix)
1064 old_suffix = self.subst('$'+old_suffix)
1066 new_prefix = self.subst('$'+new_prefix)
1067 new_suffix = self.subst('$'+new_suffix)
1069 dir,name = os.path.split(str(path))
1070 if name[:len(old_prefix)] == old_prefix:
1071 name = name[len(old_prefix):]
1072 if name[-len(old_suffix):] == old_suffix:
1073 name = name[:-len(old_suffix)]
1074 return os.path.join(dir, new_prefix+name+new_suffix)
1076 def SetDefault(self, **kw):
1078 if self._dict.has_key(k):
1080 apply(self.Replace, (), kw)
1082 def Tool(self, tool, toolpath=None, **kw):
1083 if SCons.Util.is_String(tool):
1084 tool = self.subst(tool)
1085 if toolpath is None:
1086 toolpath = self.get('toolpath', [])
1087 toolpath = map(self.subst, toolpath)
1088 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
1091 def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1092 """Find prog in the path. __cacheable__
1096 path = self['ENV']['PATH']
1099 elif SCons.Util.is_String(path):
1100 path = self.subst(path)
1103 pathext = self['ENV']['PATHEXT']
1106 elif SCons.Util.is_String(pathext):
1107 pathext = self.subst(pathext)
1108 path = SCons.Util.WhereIs(prog, path, pathext, reject)
1109 if path: return path
1112 #######################################################################
1113 # Public methods for doing real "SCons stuff" (manipulating
1114 # dependencies, setting attributes on targets, etc.). These begin
1115 # with upper-case letters. The essential characteristic of methods
1116 # in this section is that they all *should* have corresponding
1117 # same-named global functions.
1118 #######################################################################
1120 def Action(self, *args, **kw):
1121 nargs = self.subst(args)
1122 nkw = self.subst_kw(kw)
1123 return apply(SCons.Action.Action, nargs, nkw)
1125 def AddPreAction(self, files, action):
1126 nodes = self.arg2nodes(files, self.fs.Entry)
1127 action = SCons.Action.Action(action)
1129 n.add_pre_action(action)
1132 def AddPostAction(self, files, action):
1133 nodes = self.arg2nodes(files, self.fs.Entry)
1134 action = SCons.Action.Action(action)
1136 n.add_post_action(action)
1139 def Alias(self, target, source=[], action=None, **kw):
1140 tlist = self.arg2nodes(target, self.ans.Alias)
1141 if not SCons.Util.is_List(source):
1143 source = filter(None, source)
1147 # There are no source files and no action, so just
1148 # return a target list of classic Alias Nodes, without
1149 # any builder. The externally visible effect is that
1150 # this will make the wrapping Script.BuildTask class
1151 # say that there's "Nothing to be done" for this Alias,
1152 # instead of that it's "up to date."
1155 # No action, but there are sources. Re-call all the target
1156 # builders to add the sources to each target.
1159 bld = t.get_builder(AliasBuilder)
1160 result.extend(bld(self, t, source))
1163 nkw = self.subst_kw(kw)
1165 'action' : SCons.Action.Action(action),
1166 'source_factory' : self.fs.Entry,
1168 'is_explicit' : None,
1170 bld = apply(SCons.Builder.Builder, (), nkw)
1172 # Apply the Builder separately to each target so that the Aliases
1173 # stay separate. If we did one "normal" Builder call with the
1174 # whole target list, then all of the target Aliases would be
1175 # associated under a single Executor.
1178 # Calling the convert() method will cause a new Executor to be
1179 # created from scratch, so we have to explicitly initialize
1180 # it with the target's existing sources, plus our new ones,
1181 # so nothing gets lost.
1183 if b is None or b is AliasBuilder:
1186 nkw['action'] = b.action + action
1187 b = apply(SCons.Builder.Builder, (), nkw)
1189 result.extend(b(self, t, t.sources + source))
1192 def AlwaysBuild(self, *targets):
1195 tlist.extend(self.arg2nodes(t, self.fs.File))
1197 t.set_always_build()
1200 def BuildDir(self, build_dir, src_dir, duplicate=1):
1201 build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
1202 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
1203 self.fs.BuildDir(build_dir, src_dir, duplicate)
1205 def Builder(self, **kw):
1206 nkw = self.subst_kw(kw)
1207 return apply(SCons.Builder.Builder, [], nkw)
1209 def CacheDir(self, path):
1210 self.fs.CacheDir(self.subst(path))
1212 def Clean(self, targets, files):
1214 tlist = self.arg2nodes(targets, self.fs.Entry)
1215 flist = self.arg2nodes(files, self.fs.Entry)
1218 CleanTargets[t].extend(flist)
1220 CleanTargets[t] = flist
1222 def Configure(self, *args, **kw):
1225 nargs = nargs + self.subst_list(args)[0]
1226 nkw = self.subst_kw(kw)
1227 nkw['_depth'] = kw.get('_depth', 0) + 1
1229 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
1232 return apply(SCons.SConf.SConf, nargs, nkw)
1234 def Command(self, target, source, action, **kw):
1235 """Builds the supplied target files from the supplied
1236 source files using the supplied action. Action may
1237 be any type that the Builder constructor will accept
1241 'source_factory' : self.fs.Entry,
1243 try: bkw['source_scanner'] = kw['source_scanner']
1244 except KeyError: pass
1245 else: del kw['source_scanner']
1246 bld = apply(SCons.Builder.Builder, (), bkw)
1247 return apply(bld, (self, target, source), kw)
1249 def Depends(self, target, dependency):
1250 """Explicity specify that 'target's depend on 'dependency'."""
1251 tlist = self.arg2nodes(target, self.fs.Entry)
1252 dlist = self.arg2nodes(dependency, self.fs.Entry)
1254 t.add_dependency(dlist)
1257 def Dir(self, name, *args, **kw):
1260 return apply(self.fs.Dir, (self.subst(name),) + args, kw)
1262 def Entry(self, name, *args, **kw):
1265 return apply(self.fs.Entry, (self.subst(name),) + args, kw)
1267 def Environment(self, **kw):
1268 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1270 def Execute(self, action, *args, **kw):
1271 """Directly execute an action through an Environment
1273 action = apply(self.Action, (action,) + args, kw)
1274 return action([], [], self)
1276 def File(self, name, *args, **kw):
1279 return apply(self.fs.File, (self.subst(name),) + args, kw)
1281 def FindFile(self, file, dirs):
1282 file = self.subst(file)
1283 nodes = self.arg2nodes(dirs, self.fs.Dir)
1284 return SCons.Node.FS.find_file(file, tuple(nodes))
1286 def Flatten(self, sequence):
1287 return SCons.Util.flatten(sequence)
1289 def GetBuildPath(self, files):
1290 result = map(str, self.arg2nodes(files, self.fs.Entry))
1291 if SCons.Util.is_List(files):
1296 def Ignore(self, target, dependency):
1297 """Ignore a dependency."""
1298 tlist = self.arg2nodes(target, self.fs.Entry)
1299 dlist = self.arg2nodes(dependency, self.fs.Entry)
1304 def Install(self, dir, source):
1305 """Install specified files in the given directory."""
1307 dnodes = self.arg2nodes(dir, self.fs.Dir)
1309 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)
1311 sources = self.arg2nodes(source, self.fs.File)
1313 if SCons.Util.is_List(source):
1314 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))
1316 raise SCons.Errors.UserError, "Source `%s' of Install() is not a file. Install() source must be one or more files." % str(source)
1318 for dnode in dnodes:
1320 target = self.fs.File(src.name, dnode)
1321 tgt.extend(InstallBuilder(self, target, src))
1324 def InstallAs(self, target, source):
1325 """Install sources as targets."""
1326 sources = self.arg2nodes(source, self.fs.File)
1327 targets = self.arg2nodes(target, self.fs.File)
1329 for src, tgt in map(lambda x, y: (x, y), sources, targets):
1330 result.extend(InstallBuilder(self, tgt, src))
1333 def Literal(self, string):
1334 return SCons.Util.Literal(string)
1336 def Local(self, *targets):
1338 for targ in targets:
1339 if isinstance(targ, SCons.Node.Node):
1343 for t in self.arg2nodes(targ, self.fs.Entry):
1348 def Precious(self, *targets):
1351 tlist.extend(self.arg2nodes(t, self.fs.Entry))
1356 def Repository(self, *dirs, **kw):
1357 dirs = self.arg2nodes(list(dirs), self.fs.Dir)
1358 apply(self.fs.Repository, dirs, kw)
1360 def Scanner(self, *args, **kw):
1363 if SCons.Util.is_String(arg):
1364 arg = self.subst(arg)
1366 nkw = self.subst_kw(kw)
1367 return apply(SCons.Scanner.Scanner, nargs, nkw)
1369 def SConsignFile(self, name=".sconsign", dbm_module=None):
1370 if not name is None:
1371 name = self.subst(name)
1372 if not os.path.isabs(name):
1373 name = os.path.join(str(self.fs.SConstruct_dir), name)
1374 SCons.SConsign.File(name, dbm_module)
1376 def SideEffect(self, side_effect, target):
1377 """Tell scons that side_effects are built as side
1378 effects of building targets."""
1379 side_effects = self.arg2nodes(side_effect, self.fs.Entry)
1380 targets = self.arg2nodes(target, self.fs.Entry)
1382 for side_effect in side_effects:
1383 if side_effect.multiple_side_effect_has_builder():
1384 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
1385 side_effect.add_source(targets)
1386 side_effect.side_effect = 1
1387 self.Precious(side_effect)
1388 for target in targets:
1389 target.side_effects.append(side_effect)
1392 def SourceCode(self, entry, builder):
1393 """Arrange for a source code builder for (part of) a tree."""
1394 entries = self.arg2nodes(entry, self.fs.Entry)
1395 for entry in entries:
1396 entry.set_src_builder(builder)
1399 def SourceSignatures(self, type):
1400 type = self.subst(type)
1402 import SCons.Sig.MD5
1403 self._calc_module = SCons.Sig.MD5
1404 elif type == 'timestamp':
1405 import SCons.Sig.TimeStamp
1406 self._calc_module = SCons.Sig.TimeStamp
1408 raise UserError, "Unknown source signature type '%s'"%type
1410 def Split(self, arg):
1411 """This function converts a string or list into a list of strings
1412 or Nodes. This makes things easier for users by allowing files to
1413 be specified as a white-space separated list to be split.
1414 The input rules are:
1415 - A single string containing names separated by spaces. These will be
1416 split apart at the spaces.
1417 - A single Node instance
1418 - A list containing either strings or Node instances. Any strings
1419 in the list are not split at spaces.
1420 In all cases, the function returns a list of Nodes and strings."""
1421 if SCons.Util.is_List(arg):
1422 return map(self.subst, arg)
1423 elif SCons.Util.is_String(arg):
1424 return string.split(self.subst(arg))
1426 return [self.subst(arg)]
1428 def TargetSignatures(self, type):
1429 type = self.subst(type)
1431 self._build_signature = 1
1432 elif type == 'content':
1433 self._build_signature = 0
1435 raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type
1437 def Value(self, value):
1440 return SCons.Node.Python.Value(value)
1442 class OverrideEnvironment(Base):
1443 """A proxy that overrides variables in a wrapped construction
1444 environment by returning values from an overrides dictionary in
1445 preference to values from the underlying subject environment.
1447 This is a lightweight (I hope) proxy that passes through most use of
1448 attributes to the underlying Environment.Base class, but has just
1449 enough additional methods defined to act like a real construction
1450 environment with overridden values. It can wrap either a Base
1451 construction environment, or another OverrideEnvironment, which
1452 can in turn nest arbitrary OverrideEnvironments...
1454 Note that we do *not* call the underlying base class
1455 (SubsitutionEnvironment) initialization, because we get most of those
1456 from proxying the attributes of the subject construction environment.
1457 But because we subclass SubstitutionEnvironment, this class also
1458 has inherited arg2nodes() and subst*() methods; those methods can't
1459 be proxied because they need *this* object's methods to fetch the
1460 values from the overrides dictionary.
1463 __metaclass__ = SCons.Memoize.Memoized_Metaclass
1465 def __init__(self, subject, overrides={}):
1466 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
1467 self.__dict__['__subject'] = subject
1468 self.__dict__['overrides'] = overrides
1470 # Methods that make this class act like a proxy.
1471 def __getattr__(self, name):
1472 return getattr(self.__dict__['__subject'], name)
1473 def __setattr__(self, name, value):
1474 return setattr(self.__dict__['__subject'], name, value)
1476 # Methods that make this class act like a dictionary.
1477 def __getitem__(self, key):
1479 return self.__dict__['overrides'][key]
1481 return self.__dict__['__subject'].__getitem__(key)
1482 def __setitem__(self, key, value):
1483 if not SCons.Util.is_valid_construction_var(key):
1484 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
1485 self.__dict__['overrides'][key] = value
1486 def __delitem__(self, key):
1488 del self.__dict__['overrides'][key]
1491 return self.__dict__['__subject'].__delitem__(key)
1492 def get(self, key, default=None):
1493 """Emulates the get() method of dictionaries."""
1495 return self.__dict__['overrides'][key]
1497 return self.__dict__['__subject'].get(key, default)
1498 def has_key(self, key):
1500 self.__dict__['overrides'][key]
1503 return self.__dict__['__subject'].has_key(key)
1504 def Dictionary(self):
1505 """Emulates the items() method of dictionaries."""
1506 d = self.__dict__['__subject'].Dictionary().copy()
1507 d.update(self.__dict__['overrides'])
1510 """Emulates the items() method of dictionaries."""
1511 return self.Dictionary().items()
1513 # Overridden private construction environment methods.
1514 def _update(self, dict):
1515 """Update an environment's values directly, bypassing the normal
1516 checks that occur when users try to set items.
1518 self.__dict__['overrides'].update(dict)
1521 return self.__dict__['__subject'].gvars()
1524 lvars = self.__dict__['__subject'].lvars()
1525 lvars.update(self.__dict__['overrides'])
1528 # Overridden public construction environment methods.
1529 def Replace(self, **kw):
1530 kw = copy_non_reserved_keywords(kw)
1531 self.__dict__['overrides'].update(our_deepcopy(kw))
1533 # The entry point that will be used by the external world
1534 # to refer to a construction environment. This allows the wrapper
1535 # interface to extend a construction environment for its own purposes
1536 # by subclassing SCons.Environment.Base and then assigning the
1537 # class to SCons.Environment.Environment.
1541 # An entry point for returning a proxy subclass instance that overrides
1542 # the subst*() methods so they don't actually perform construction
1543 # variable substitution. This is specifically intended to be the shim
1544 # layer in between global function calls (which don't want construction
1545 # variable substitution) and the DefaultEnvironment() (which would
1546 # substitute variables if left to its own devices)."""
1548 # We have to wrap this in a function that allows us to delay definition of
1549 # the class until it's necessary, so that when it subclasses Environment
1550 # it will pick up whatever Environment subclass the wrapper interface
1551 # might have assigned to SCons.Environment.Environment.
1553 def NoSubstitutionProxy(subject):
1554 class _NoSubstitutionProxy(Environment):
1555 def __init__(self, subject):
1556 self.__dict__['__subject'] = subject
1557 def __getattr__(self, name):
1558 return getattr(self.__dict__['__subject'], name)
1559 def __setattr__(self, name, value):
1560 return setattr(self.__dict__['__subject'], name, value)
1561 def raw_to_mode(self, dict):
1569 def subst(self, string, *args, **kwargs):
1571 def subst_kw(self, kw, *args, **kwargs):
1573 def subst_list(self, string, *args, **kwargs):
1574 nargs = (string, self,) + args
1577 self.raw_to_mode(nkw)
1578 return apply(SCons.Util.scons_subst_list, nargs, nkw)
1579 def subst_target_source(self, string, *args, **kwargs):
1580 nargs = (string, self,) + args
1583 self.raw_to_mode(nkw)
1584 return apply(SCons.Util.scons_subst, nargs, nkw)
1585 return _NoSubstitutionProxy(subject)
1587 if not SCons.Memoize.has_metaclass:
1589 class Base(SCons.Memoize.Memoizer, _Base):
1590 def __init__(self, *args, **kw):
1591 SCons.Memoize.Memoizer.__init__(self)
1592 apply(_Base.__init__, (self,)+args, kw)