SCons.Environment

Package SCons :: Module Environment |
[hide private]
[frames] | no frames]

# Source Code for Module SCons.Environment

       1  """SCons.Environment
       2
       3  Base class for construction Environments.  These are
       4  the primary objects used to communicate dependency and
       5  construction information to the build engine.
       6
       7  Keyword arguments supplied when the construction Environment
       8  is created are construction variables used to initialize the
       9  Environment
      10  """
      11
      12  #
      13  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
      14  #
      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:
      22  #
      23  # The above copyright notice and this permission notice shall be included
      24  # in all copies or substantial portions of the Software.
      25  #
      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.
      33  #
      34
      35  __revision__ = "src/engine/SCons/Environment.py 2725 2008/03/31 12:52:02 knight"
      36
      37
      38  import copy
      39  import os
      40  import os.path
      41  import re
      42  import shlex
      43  import string
      44  from UserDict import UserDict
      45
      46  import SCons.Action
      47  import SCons.Builder
      48  from SCons.Debug import logInstanceCreation
      49  import SCons.Defaults
      50  import SCons.Errors
      51  import SCons.Memoize
      52  import SCons.Node
      53  import SCons.Node.Alias
      54  import SCons.Node.FS
      55  import SCons.Node.Python
      56  import SCons.Platform
      57  import SCons.SConsign
      58  import SCons.Subst
      59  import SCons.Tool
      60  import SCons.Util
      61  import SCons.Warnings
      62
      63 -class _Null:
      64      pass
      65
      66  _null = _Null
      67
      68  _warn_copy_deprecated = True
      69  _warn_source_signatures_deprecated = True
      70  _warn_target_signatures_deprecated = True
      71
      72  CleanTargets = {}
      73  CalculatorArgs = {}
      74
      75  semi_deepcopy = SCons.Util.semi_deepcopy
      76
      77  # Pull UserError into the global name space for the benefit of
      78  # Environment().SourceSignatures(), which has some import statements
      79  # which seem to mess up its ability to reference SCons directly.
      80  UserError = SCons.Errors.UserError
      81
      82 -def alias_builder(env, target, source):
      83      pass
      84
      85  AliasBuilder = SCons.Builder.Builder(action = alias_builder,
      86                                       target_factory = SCons.Node.Alias.default_ans.Alias,
      87                                       source_factory = SCons.Node.FS.Entry,
      88                                       multi = 1,
      89                                       is_explicit = None,
      90                                       name='AliasBuilder')
      91
      92 -def apply_tools(env, tools, toolpath):
      93      # Store the toolpath in the Environment.
      94      if toolpath is not None:
      95          env['toolpath'] = toolpath
      96
      97      if not tools:
      98          return
      99      # Filter out null tools from the list.
     100      for tool in filter(None, tools):
     101          if SCons.Util.is_List(tool) or type(tool)==type(()):
     102              toolname = tool[0]
     103              toolargs = tool[1] # should be a dict of kw args
     104              tool = apply(env.Tool, [toolname], toolargs)
     105          else:
     106              env.Tool(tool)
     107
     108  # These names are controlled by SCons; users should never set or override
     109  # them.  This warning can optionally be turned off, but scons will still
     110  # ignore the illegal variable names even if it's off.
     111  reserved_construction_var_names = \
     112      ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
     113
     114 -def copy_non_reserved_keywords(dict):
     115      result = semi_deepcopy(dict)
     116      for k in result.keys():
     117          if k in reserved_construction_var_names:
     118              SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
     119                                  "Ignoring attempt to set reserved variable `%s'" % k)
     120              del result[k]
     121      return result
     122
     123 -def _set_reserved(env, key, value):
     124      msg = "Ignoring attempt to set reserved variable `%s'" % key
     125      SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg)
     126
     127 -def _set_BUILDERS(env, key, value):
     128      try:
     129          bd = env._dict[key]
     130          for k in bd.keys():
     131              del bd[k]
     132      except KeyError:
     133          bd = BuilderDict(kwbd, env)
     134          env._dict[key] = bd
     135      bd.update(value)
     136
     137 -def _del_SCANNERS(env, key):
     138      del env._dict[key]
     139      env.scanner_map_delete()
     140
     141 -def _set_SCANNERS(env, key, value):
     142      env._dict[key] = value
     143      env.scanner_map_delete()
     144
     145
     146
     147  # The following is partly based on code in a comment added by Peter
     148  # Shannon at the following page (there called the "transplant" class):
     149  #
     150  # ASPN : Python Cookbook : Dynamically added methods to a class
     151  # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
     152  #
     153  # We had independently been using the idiom as BuilderWrapper, but
     154  # factoring out the common parts into this base class, and making
     155  # BuilderWrapper a subclass that overrides __call__() to enforce specific
     156  # Builder calling conventions, simplified some of our higher-layer code.
     157
     158 -class MethodWrapper:
     159      """
     160      A generic Wrapper class that associates a method (which can
     161      actually be any callable) with an object.  As part of creating this
     162      MethodWrapper object an attribute with the specified (by default,
     163      the name of the supplied method) is added to the underlying object.
     164      When that new "method" is called, our __call__() method adds the
     165      object as the first argument, simulating the Python behavior of
     166      supplying "self" on method calls.
     167
     168      We hang on to the name by which the method was added to the underlying
     169      base class so that we can provide a method to "clone" ourselves onto
     170      a new underlying object being copied (without which we wouldn't need
     171      to save that info).
     172      """
     173 -    def __init__(self, object, method, name=None):
     174          if name is None:
     175              name = method.__name__
     176          self.object = object
     177          self.method = method
     178          self.name = name
     179          setattr(self.object, name, self)
     180
     181 -    def __call__(self, *args, **kwargs):
     182          nargs = (self.object,) + args
     183          return apply(self.method, nargs, kwargs)
     184
     185 -    def clone(self, new_object):
     186          """
     187          Returns an object that re-binds the underlying "method" to
     188          the specified new object.
     189          """
     190          return self.__class__(new_object, self.method, self.name)
     191
     192 -class BuilderWrapper(MethodWrapper):
     193      """
     194      A MethodWrapper subclass that that associates an environment with
     195      a Builder.
     196
     197      This mainly exists to wrap the __call__() function so that all calls
     198      to Builders can have their argument lists massaged in the same way
     199      (treat a lone argument as the source, treat two arguments as target
     200      then source, make sure both target and source are lists) without
     201      having to have cut-and-paste code to do it.
     202
     203      As a bit of obsessive backwards compatibility, we also intercept
     204      attempts to get or set the "env" or "builder" attributes, which were
     205      the names we used before we put the common functionality into the
     206      MethodWrapper base class.  We'll keep this around for a while in case
     207      people shipped Tool modules that reached into the wrapper (like the
     208      Tool/qt.py module does, or did).  There shouldn't be a lot attribute
     209      fetching or setting on these, so a little extra work shouldn't hurt.
     210      """
     211 -    def __call__(self, target=None, source=_null, *args, **kw):
     212          if source is _null:
     213              source = target
     214              target = None
     215          if not target is None and not SCons.Util.is_List(target):
     216              target = [target]
     217          if not source is None and not SCons.Util.is_List(source):
     218              source = [source]
     219          return apply(MethodWrapper.__call__, (self, target, source) + args, kw)
     220
     221 -    def __repr__(self):
     222          return '<BuilderWrapper %s>' % repr(self.name)
     223
     224 -    def __str__(self):
     225          return self.__repr__()
     226
     227 -    def __getattr__(self, name):
     228          if name == 'env':
     229              return self.object
     230          elif name == 'builder':
     231              return self.method
     232          else:
     233              return self.__dict__[name]
     234
     235 -    def __setattr__(self, name, value):
     236          if name == 'env':
     237              self.object = value
     238          elif name == 'builder':
     239              self.method = value
     240          else:
     241              self.__dict__[name] = value
     242
     243      # This allows a Builder to be executed directly
     244      # through the Environment to which it's attached.
     245      # In practice, we shouldn't need this, because
     246      # builders actually get executed through a Node.
     247      # But we do have a unit test for this, and can't
     248      # yet rule out that it would be useful in the
     249      # future, so leave it for now.
     250      #def execute(self, **kw):
     251      #    kw['env'] = self.env
     252      #    apply(self.builder.execute, (), kw)
     253
     254 -class BuilderDict(UserDict):
     255      """This is a dictionary-like class used by an Environment to hold
     256      the Builders.  We need to do this because every time someone changes
     257      the Builders in the Environment's BUILDERS dictionary, we must
     258      update the Environment's attributes."""
     259 -    def __init__(self, dict, env):
     260          # Set self.env before calling the superclass initialization,
     261          # because it will end up calling our other methods, which will
     262          # need to point the values in this dictionary to self.env.
     263          self.env = env
     264          UserDict.__init__(self, dict)
     265
     266 -    def __semi_deepcopy__(self):
     267          return self.__class__(self.data, self.env)
     268
     269 -    def __setitem__(self, item, val):
     270          try:
     271              method = getattr(self.env, item).method
     272          except AttributeError:
     273              pass
     274          else:
     275              self.env.RemoveMethod(method)
     276          UserDict.__setitem__(self, item, val)
     277          BuilderWrapper(self.env, val, item)
     278
     279 -    def __delitem__(self, item):
     280          UserDict.__delitem__(self, item)
     281          delattr(self.env, item)
     282
     283 -    def update(self, dict):
     284          for i, v in dict.items():
     285              self.__setitem__(i, v)
     286
     287
     288
     289  _is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
     290
     291 -def is_valid_construction_var(varstr):
     292      """Return if the specified string is a legitimate construction
     293      variable.
     294      """
     295      return _is_valid_var.match(varstr)
     296
     297
     298
     299 -class SubstitutionEnvironment:
     300      """Base class for different flavors of construction environments.
     301
     302      This class contains a minimal set of methods that handle contruction
     303      variable expansion and conversion of strings to Nodes, which may or
     304      may not be actually useful as a stand-alone class.  Which methods
     305      ended up in this class is pretty arbitrary right now.  They're
     306      basically the ones which we've empirically determined are common to
     307      the different construction environment subclasses, and most of the
     308      others that use or touch the underlying dictionary of construction
     309      variables.
     310
     311      Eventually, this class should contain all the methods that we
     312      determine are necessary for a "minimal" interface to the build engine.
     313      A full "native Python" SCons environment has gotten pretty heavyweight
     314      with all of the methods and Tools and construction variables we've
     315      jammed in there, so it would be nice to have a lighter weight
     316      alternative for interfaces that don't need all of the bells and
     317      whistles.  (At some point, we'll also probably rename this class
     318      "Base," since that more reflects what we want this class to become,
     319      but because we've released comments that tell people to subclass
     320      Environment.Base to create their own flavors of construction
     321      environment, we'll save that for a future refactoring when this
     322      class actually becomes useful.)
     323      """
     324
     325      if SCons.Memoize.use_memoizer:
     326          __metaclass__ = SCons.Memoize.Memoized_Metaclass
     327
     328 -    def __init__(self, **kw):
     329          """Initialization of an underlying SubstitutionEnvironment class.
     330          """
     331          if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment')
     332          self.fs = SCons.Node.FS.get_default_fs()
     333          self.ans = SCons.Node.Alias.default_ans
     334          self.lookup_list = SCons.Node.arg2nodes_lookups
     335          self._dict = kw.copy()
     336          self._init_special()
     337          self.added_methods = []
     338          #self._memo = {}
     339
     340 -    def _init_special(self):
     341          """Initial the dispatch tables for special handling of
     342          special construction variables."""
     343          self._special_del = {}
     344          self._special_del['SCANNERS'] = _del_SCANNERS
     345
     346          self._special_set = {}
     347          for key in reserved_construction_var_names:
     348              self._special_set[key] = _set_reserved
     349          self._special_set['BUILDERS'] = _set_BUILDERS
     350          self._special_set['SCANNERS'] = _set_SCANNERS
     351
     352          # Freeze the keys of self._special_set in a list for use by
     353          # methods that need to check.  (Empirically, list scanning has
     354          # gotten better than dict.has_key() in Python 2.5.)
     355          self._special_set_keys = self._special_set.keys()
     356
     357 -    def __cmp__(self, other):
     358          return cmp(self._dict, other._dict)
     359
     360 -    def __delitem__(self, key):
     361          special = self._special_del.get(key)
     362          if special:
     363              special(self, key)
     364          else:
     365              del self._dict[key]
     366
     367 -    def __getitem__(self, key):
     368          return self._dict[key]
     369
     370 -    def __setitem__(self, key, value):
     371          # This is heavily used.  This implementation is the best we have
     372          # according to the timings in bench/env.__setitem__.py.
     373          #
     374          # The "key in self._special_set_keys" test here seems to perform
     375          # pretty well for the number of keys we have.  A hard-coded
     376          # list works a little better in Python 2.5, but that has the
     377          # disadvantage of maybe getting out of sync if we ever add more
     378          # variable names.  Using self._special_set.has_key() works a
     379          # little better in Python 2.4, but is worse then this test.
     380          # So right now it seems like a good trade-off, but feel free to
     381          # revisit this with bench/env.__setitem__.py as needed (and
     382          # as newer versions of Python come out).
     383          if key in self._special_set_keys:
     384              self._special_set[key](self, key, value)
     385          else:
     386              # If we already have the entry, then it's obviously a valid
     387              # key and we don't need to check.  If we do check, using a
     388              # global, pre-compiled regular expression directly is more
     389              # efficient than calling another function or a method.
     390              if not self._dict.has_key(key) \
     391                 and not _is_valid_var.match(key):
     392                      raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
     393              self._dict[key] = value
     394
     395 -    def get(self, key, default=None):
     396          "Emulates the get() method of dictionaries."""
     397          return self._dict.get(key, default)
     398
     399 -    def has_key(self, key):
     400          return self._dict.has_key(key)
     401
     402 -    def items(self):
     403          return self._dict.items()
     404
     405 -    def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
     406          if node_factory is _null:
     407              node_factory = self.fs.File
     408          if lookup_list is _null:
     409              lookup_list = self.lookup_list
     410
     411          if not args:
     412              return []
     413
     414          args = SCons.Util.flatten(args)
     415
     416          nodes = []
     417          for v in args:
     418              if SCons.Util.is_String(v):
     419                  n = None
     420                  for l in lookup_list:
     421                      n = l(v)
     422                      if not n is None:
     423                          break
     424                  if not n is None:
     425                      if SCons.Util.is_String(n):
     426                          # n = self.subst(n, raw=1, **kw)
     427                          kw['raw'] = 1
     428                          n = apply(self.subst, (n,), kw)
     429                          if node_factory:
     430                              n = node_factory(n)
     431                      if SCons.Util.is_List(n):
     432                          nodes.extend(n)
     433                      else:
     434                          nodes.append(n)
     435                  elif node_factory:
     436                      # v = node_factory(self.subst(v, raw=1, **kw))
     437                      kw['raw'] = 1
     438                      v = node_factory(apply(self.subst, (v,), kw))
     439                      if SCons.Util.is_List(v):
     440                          nodes.extend(v)
     441                      else:
     442                          nodes.append(v)
     443              else:
     444                  nodes.append(v)
     445
     446          return nodes
     447
     448 -    def gvars(self):
     449          return self._dict
     450
     451 -    def lvars(self):
     452          return {}
     453
     454 -    def subst(self, string, raw=0, target=None, source=None, conv=None):
     455          """Recursively interpolates construction variables from the
     456          Environment into the specified string, returning the expanded
     457          result.  Construction variables are specified by a $ prefix
     458          in the string and begin with an initial underscore or
     459          alphabetic character followed by any number of underscores
     460          or alphanumeric characters.  The construction variable names
     461          may be surrounded by curly braces to separate the name from
     462          trailing characters.
     463          """
     464          gvars = self.gvars()
     465          lvars = self.lvars()
     466          lvars['__env__'] = self
     467          return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
     468
     469 -    def subst_kw(self, kw, raw=0, target=None, source=None):
     470          nkw = {}
     471          for k, v in kw.items():
     472              k = self.subst(k, raw, target, source)
     473              if SCons.Util.is_String(v):
     474                  v = self.subst(v, raw, target, source)
     475              nkw[k] = v
     476          return nkw
     477
     478 -    def subst_list(self, string, raw=0, target=None, source=None, conv=None):
     479          """Calls through to SCons.Subst.scons_subst_list().  See
     480          the documentation for that function."""
     481          gvars = self.gvars()
     482          lvars = self.lvars()
     483          lvars['__env__'] = self
     484          return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
     485
     486 -    def subst_path(self, path, target=None, source=None):
     487          """Substitute a path list, turning EntryProxies into Nodes
     488          and leaving Nodes (and other objects) as-is."""
     489
     490          if not SCons.Util.is_List(path):
     491              path = [path]
     492
     493          def s(obj):
     494              """This is the "string conversion" routine that we have our
     495              substitutions use to return Nodes, not strings.  This relies
     496              on the fact that an EntryProxy object has a get() method that
     497              returns the underlying Node that it wraps, which is a bit of
     498              architectural dependence that we might need to break or modify
     499              in the future in response to additional requirements."""
     500              try:
     501                  get = obj.get
     502              except AttributeError:
     503                  obj = SCons.Util.to_String_for_subst(obj)
     504              else:
     505                  obj = get()
     506              return obj
     507
     508          r = []
     509          for p in path:
     510              if SCons.Util.is_String(p):
     511                  p = self.subst(p, target=target, source=source, conv=s)
     512                  if SCons.Util.is_List(p):
     513                      if len(p) == 1:
     514                          p = p[0]
     515                      else:
     516                          # We have an object plus a string, or multiple
     517                          # objects that we need to smush together.  No choice
     518                          # but to make them into a string.
     519                          p = string.join(map(SCons.Util.to_String_for_subst, p), '')
     520              else:
     521                  p = s(p)
     522              r.append(p)
     523          return r
     524
     525      subst_target_source = subst
     526
     527 -    def backtick(self, command):
     528          import subprocess
     529          if SCons.Util.is_List(command):
     530              p = subprocess.Popen(command,
     531                                   stdout=subprocess.PIPE,
     532                                   stderr=subprocess.PIPE,
     533                                   universal_newlines=True)
     534          else:
     535              p = subprocess.Popen(command,
     536                                   stdout=subprocess.PIPE,
     537                                   stderr=subprocess.PIPE,
     538                                   universal_newlines=True,
     539                                   shell=True)
     540          out = p.stdout.read()
     541          p.stdout.close()
     542          err = p.stderr.read()
     543          p.stderr.close()
     544          status = p.wait()
     545          if err:
     546              import sys
     547              sys.stderr.write(err)
     548          if status:
     549              raise OSError("'%s' exited %d" % (command, status))
     550          return out
     551
     552 -    def AddMethod(self, function, name=None):
     553          """
     554          Adds the specified function as a method of this construction
     555          environment with the specified name.  If the name is omitted,
     556          the default name is the name of the function itself.
     557          """
     558          method = MethodWrapper(self, function, name)
     559          self.added_methods.append(method)
     560
     561 -    def RemoveMethod(self, function):
     562          """
     563          Removes the specified function's MethodWrapper from the
     564          added_methods list, so we don't re-bind it when making a clone.
     565          """
     566          is_not_func = lambda dm, f=function: not dm.method is f
     567          self.added_methods = filter(is_not_func, self.added_methods)
     568
     569 -    def Override(self, overrides):
     570          """
     571          Produce a modified environment whose variables are overriden by
     572          the overrides dictionaries.  "overrides" is a dictionary that
     573          will override the variables of this environment.
     574
     575          This function is much more efficient than Clone() or creating
     576          a new Environment because it doesn't copy the construction
     577          environment dictionary, it just wraps the underlying construction
     578          environment, and doesn't even create a wrapper object if there
     579          are no overrides.
     580          """
     581          if not overrides: return self
     582          o = copy_non_reserved_keywords(overrides)
     583          if not o: return self
     584          overrides = {}
     585          merges = None
     586          for key, value in o.items():
     587              if key == 'parse_flags':
     588                  merges = value
     589              else:
     590                  overrides[key] = SCons.Subst.scons_subst_once(value, self, key)
     591          env = OverrideEnvironment(self, overrides)
     592          if merges: env.MergeFlags(merges)
     593          return env
     594
     595 -    def ParseFlags(self, *flags):
     596          """
     597          Parse the set of flags and return a dict with the flags placed
     598          in the appropriate entry.  The flags are treated as a typical
     599          set of command-line flags for a GNU-like toolchain and used to
     600          populate the entries in the dict immediately below.  If one of
     601          the flag strings begins with a bang (exclamation mark), it is
     602          assumed to be a command and the rest of the string is executed;
     603          the result of that evaluation is then added to the dict.
     604          """
     605          dict = {
     606              'ASFLAGS'       : SCons.Util.CLVar(''),
     607              'CFLAGS'        : SCons.Util.CLVar(''),
     608              'CCFLAGS'       : SCons.Util.CLVar(''),
     609              'CPPDEFINES'    : [],
     610              'CPPFLAGS'      : SCons.Util.CLVar(''),
     611              'CPPPATH'       : [],
     612              'FRAMEWORKPATH' : SCons.Util.CLVar(''),
     613              'FRAMEWORKS'    : SCons.Util.CLVar(''),
     614              'LIBPATH'       : [],
     615              'LIBS'          : [],
     616              'LINKFLAGS'     : SCons.Util.CLVar(''),
     617              'RPATH'         : [],
     618          }
     619
     620          # The use of the "me" parameter to provide our own name for
     621          # recursion is an egregious hack to support Python 2.1 and before.
     622          def do_parse(arg, me, self = self, dict = dict):
     623              # if arg is a sequence, recurse with each element
     624              if not arg:
     625                  return
     626
     627              if not SCons.Util.is_String(arg):
     628                  for t in arg: me(t, me)
     629                  return
     630
     631              # if arg is a command, execute it
     632              if arg[0] == '!':
     633                  arg = self.backtick(arg[1:])
     634
     635              # utility function to deal with -D option
     636              def append_define(name, dict = dict):
     637                  t = string.split(name, '=')
     638                  if len(t) == 1:
     639                      dict['CPPDEFINES'].append(name)
     640                  else:
     641                      dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
     642
     643              # Loop through the flags and add them to the appropriate option.
     644              # This tries to strike a balance between checking for all possible
     645              # flags and keeping the logic to a finite size, so it doesn't
     646              # check for some that don't occur often.  It particular, if the
     647              # flag is not known to occur in a config script and there's a way
     648              # of passing the flag to the right place (by wrapping it in a -W
     649              # flag, for example) we don't check for it.  Note that most
     650              # preprocessor options are not handled, since unhandled options
     651              # are placed in CCFLAGS, so unless the preprocessor is invoked
     652              # separately, these flags will still get to the preprocessor.
     653              # Other options not currently handled:
     654              #  -iqoutedir      (preprocessor search path)
     655              #  -u symbol       (linker undefined symbol)
     656              #  -s              (linker strip files)
     657              #  -static*        (linker static binding)
     658              #  -shared*        (linker dynamic binding)
     659              #  -symbolic       (linker global binding)
     660              #  -R dir          (deprecated linker rpath)
     661              # IBM compilers may also accept -qframeworkdir=foo
     662
     663              params = shlex.split(arg)
     664              append_next_arg_to = None   # for multi-word args
     665              for arg in params:
     666                  if append_next_arg_to:
     667                     if append_next_arg_to == 'CPPDEFINES':
     668                         append_define(arg)
     669                     elif append_next_arg_to == '-include':
     670                         t = ('-include', self.fs.File(arg))
     671                         dict['CCFLAGS'].append(t)
     672                     elif append_next_arg_to == '-isysroot':
     673                         t = ('-isysroot', arg)
     674                         dict['CCFLAGS'].append(t)
     675                         dict['LINKFLAGS'].append(t)
     676                     elif append_next_arg_to == '-arch':
     677                         t = ('-arch', arg)
     678                         dict['CCFLAGS'].append(t)
     679                         dict['LINKFLAGS'].append(t)
     680                     else:
     681                         dict[append_next_arg_to].append(arg)
     682                     append_next_arg_to = None
     683                  elif not arg[0] in ['-', '+']:
     684                      dict['LIBS'].append(self.fs.File(arg))
     685                  elif arg[:2] == '-L':
     686                      if arg[2:]:
     687                          dict['LIBPATH'].append(arg[2:])
     688                      else:
     689                          append_next_arg_to = 'LIBPATH'
     690                  elif arg[:2] == '-l':
     691                      if arg[2:]:
     692                          dict['LIBS'].append(arg[2:])
     693                      else:
     694                          append_next_arg_to = 'LIBS'
     695                  elif arg[:2] == '-I':
     696                      if arg[2:]:
     697                          dict['CPPPATH'].append(arg[2:])
     698                      else:
     699                          append_next_arg_to = 'CPPPATH'
     700                  elif arg[:4] == '-Wa,':
     701                      dict['ASFLAGS'].append(arg[4:])
     702                      dict['CCFLAGS'].append(arg)
     703                  elif arg[:4] == '-Wl,':
     704                      if arg[:11] == '-Wl,-rpath=':
     705                          dict['RPATH'].append(arg[11:])
     706                      elif arg[:7] == '-Wl,-R,':
     707                          dict['RPATH'].append(arg[7:])
     708                      elif arg[:6] == '-Wl,-R':
     709                          dict['RPATH'].append(arg[6:])
     710                      else:
     711                          dict['LINKFLAGS'].append(arg)
     712                  elif arg[:4] == '-Wp,':
     713                      dict['CPPFLAGS'].append(arg)
     714                  elif arg[:2] == '-D':
     715                      if arg[2:]:
     716                          append_define(arg[2:])
     717                      else:
     718                          append_next_arg_to = 'CPPDEFINES'
     719                  elif arg == '-framework':
     720                      append_next_arg_to = 'FRAMEWORKS'
     721                  elif arg[:14] == '-frameworkdir=':
     722                      dict['FRAMEWORKPATH'].append(arg[14:])
     723                  elif arg[:2] == '-F':
     724                      if arg[2:]:
     725                          dict['FRAMEWORKPATH'].append(arg[2:])
     726                      else:
     727                          append_next_arg_to = 'FRAMEWORKPATH'
     728                  elif arg == '-mno-cygwin':
     729                      dict['CCFLAGS'].append(arg)
     730                      dict['LINKFLAGS'].append(arg)
     731                  elif arg == '-mwindows':
     732                      dict['LINKFLAGS'].append(arg)
     733                  elif arg == '-pthread':
     734                      dict['CCFLAGS'].append(arg)
     735                      dict['LINKFLAGS'].append(arg)
     736                  elif arg[:5] == '-std=':
     737                      dict['CFLAGS'].append(arg) # C only
     738                  elif arg[0] == '+':
     739                      dict['CCFLAGS'].append(arg)
     740                      dict['LINKFLAGS'].append(arg)
     741                  elif arg in ['-include', '-isysroot', '-arch']:
     742                      append_next_arg_to = arg
     743                  else:
     744                      dict['CCFLAGS'].append(arg)
     745
     746          for arg in flags:
     747              do_parse(arg, do_parse)
     748          return dict
     749
     750 -    def MergeFlags(self, args, unique=1):
     751          """
     752          Merge the dict in args into the construction variables.  If args
     753          is not a dict, it is converted into a dict using ParseFlags.
     754          If unique is not set, the flags are appended rather than merged.
     755          """
     756
     757          if not SCons.Util.is_Dict(args):
     758              args = self.ParseFlags(args)
     759          if not unique:
     760              apply(self.Append, (), args)
     761              return self
     762          for key, value in args.items():
     763              if not value:
     764                  continue
     765              try:
     766                  orig = self[key]
     767              except KeyError:
     768                  orig = value
     769              else:
     770                  if not orig:
     771                      orig = value
     772                  elif value:
     773                      # Add orig and value.  The logic here was lifted from
     774                      # part of env.Append() (see there for a lot of comments
     775                      # about the order in which things are tried) and is
     776                      # used mainly to handle coercion of strings to CLVar to
     777                      # "do the right thing" given (e.g.) an original CCFLAGS
     778                      # string variable like '-pipe -Wall'.
     779                      try:
     780                          orig = orig + value
     781                      except (KeyError, TypeError):
     782                          try:
     783                              add_to_orig = orig.append
     784                          except AttributeError:
     785                              value.insert(0, orig)
     786                              orig = value
     787                          else:
     788                              add_to_orig(value)
     789              t = []
     790              if key[-4:] == 'PATH':
     791                  ### keep left-most occurence
     792                  for v in orig:
     793                      if v not in t:
     794                          t.append(v)
     795              else:
     796                  ### keep right-most occurence
     797                  orig.reverse()
     798                  for v in orig:
     799                      if v not in t:
     800                          t.insert(0, v)
     801              self[key] = t
     802          return self
     803
     804  # Used by the FindSourceFiles() method, below.
     805  # Stuck here for support of pre-2.2 Python versions.
     806 -def build_source(ss, result):
     807      for s in ss:
     808          if isinstance(s, SCons.Node.FS.Dir):
     809              build_source(s.all_children(), result)
     810          elif s.has_builder():
     811              build_source(s.sources, result)
     812          elif isinstance(s.disambiguate(), SCons.Node.FS.File):
     813              result.append(s)
     814
     815 -def default_decide_source(dependency, target, prev_ni):
     816      f = SCons.Defaults.DefaultEnvironment().decide_source
     817      return f(dependency, target, prev_ni)
     818
     819 -def default_decide_target(dependency, target, prev_ni):
     820      f = SCons.Defaults.DefaultEnvironment().decide_target
     821      return f(dependency, target, prev_ni)
     822
     823 -def default_copy_from_cache(src, dst):
     824      f = SCons.Defaults.DefaultEnvironment().copy_from_cache
     825      return f(src, dst)
     826
     827 -class Base(SubstitutionEnvironment):
     828      """Base class for "real" construction Environments.  These are the
     829      primary objects used to communicate dependency and construction
     830      information to the build engine.
     831
     832      Keyword arguments supplied when the construction Environment
     833      is created are construction variables used to initialize the
     834      Environment.
     835      """
     836
     837      if SCons.Memoize.use_memoizer:
     838          __metaclass__ = SCons.Memoize.Memoized_Metaclass
     839
     840      memoizer_counters = []
     841
     842      #######################################################################
     843      # This is THE class for interacting with the SCons build engine,
     844      # and it contains a lot of stuff, so we're going to try to keep this
     845      # a little organized by grouping the methods.
     846      #######################################################################
     847
     848      #######################################################################
     849      # Methods that make an Environment act like a dictionary.  These have
     850      # the expected standard names for Python mapping objects.  Note that
     851      # we don't actually make an Environment a subclass of UserDict for
     852      # performance reasons.  Note also that we only supply methods for
     853      # dictionary functionality that we actually need and use.
     854      #######################################################################
     855
     856 -    def __init__(self,
     857                   platform=None,
     858                   tools=None,
     859                   toolpath=None,
     860                   options=None,
     861                   parse_flags = None,
     862                   **kw):
     863          """
     864          Initialization of a basic SCons construction environment,
     865          including setting up special construction variables like BUILDER,
     866          PLATFORM, etc., and searching for and applying available Tools.
     867
     868          Note that we do *not* call the underlying base class
     869          (SubsitutionEnvironment) initialization, because we need to
     870          initialize things in a very specific order that doesn't work
     871          with the much simpler base class initialization.
     872          """
     873          if __debug__: logInstanceCreation(self, 'Environment.Base')
     874          self._memo = {}
     875          self.fs = SCons.Node.FS.get_default_fs()
     876          self.ans = SCons.Node.Alias.default_ans
     877          self.lookup_list = SCons.Node.arg2nodes_lookups
     878          self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment)
     879          self._init_special()
     880          self.added_methods = []
     881
     882          # We don't use AddMethod, or define these as methods in this
     883          # class, because we *don't* want these functions to be bound
     884          # methods.  They need to operate independently so that the
     885          # settings will work properly regardless of whether a given
     886          # target ends up being built with a Base environment or an
     887          # OverrideEnvironment or what have you.
     888          self.decide_target = default_decide_target
     889          self.decide_source = default_decide_source
     890
     891          self.copy_from_cache = default_copy_from_cache
     892
     893          self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self)
     894
     895          if platform is None:
     896              platform = self._dict.get('PLATFORM', None)
     897              if platform is None:
     898                  platform = SCons.Platform.Platform()
     899          if SCons.Util.is_String(platform):
     900              platform = SCons.Platform.Platform(platform)
     901          self._dict['PLATFORM'] = str(platform)
     902          platform(self)
     903
     904          # Apply the passed-in variables and customizable options to the
     905          # environment before calling the tools, because they may use
     906          # some of them during initialization.
     907          apply(self.Replace, (), kw)
     908          keys = kw.keys()
     909          if options:
     910              keys = keys + options.keys()
     911              options.Update(self)
     912
     913          save = {}
     914          for k in keys:
     915              try:
     916                  save[k] = self._dict[k]
     917              except KeyError:
     918                  # No value may have been set if they tried to pass in a
     919                  # reserved variable name like TARGETS.
     920                  pass
     921
     922          SCons.Tool.Initializers(self)
     923
     924          if tools is None:
     925              tools = self._dict.get('TOOLS', None)
     926              if tools is None:
     927                  tools = ['default']
     928          apply_tools(self, tools, toolpath)
     929
     930          # Now restore the passed-in variables and customized options
     931          # to the environment, since the values the user set explicitly
     932          # should override any values set by the tools.
     933          for key, val in save.items():
     934              self._dict[key] = val
     935
     936          # Finally, apply any flags to be merged in
     937          if parse_flags: self.MergeFlags(parse_flags)
     938
     939      #######################################################################
     940      # Utility methods that are primarily for internal use by SCons.
     941      # These begin with lower-case letters.
     942      #######################################################################
     943
     944 -    def get_builder(self, name):
     945          """Fetch the builder with the specified name from the environment.
     946          """
     947          try:
     948              return self._dict['BUILDERS'][name]
     949          except KeyError:
     950              return None
     951
     952 -    def get_CacheDir(self):
     953          try:
     954              path = self._CacheDir_path
     955          except AttributeError:
     956              path = SCons.Defaults.DefaultEnvironment()._CacheDir_path
     957          try:
     958              if path == self._last_CacheDir_path:
     959                  return self._last_CacheDir
     960          except AttributeError:
     961              pass
     962          cd = SCons.CacheDir.CacheDir(path)
     963          self._last_CacheDir_path = path
     964          self._last_CacheDir = cd
     965          return cd
     966
     967 -    def get_factory(self, factory, default='File'):
     968          """Return a factory function for creating Nodes for this
     969          construction environment.
     970          """
     971          name = default
     972          try:
     973              is_node = issubclass(factory, SCons.Node.Node)
     974          except TypeError:
     975              # The specified factory isn't a Node itself--it's
     976              # most likely None, or possibly a callable.
     977              pass
     978          else:
     979              if is_node:
     980                  # The specified factory is a Node (sub)class.  Try to
     981                  # return the FS method that corresponds to the Node's
     982                  # name--that is, we return self.fs.Dir if they want a Dir,
     983                  # self.fs.File for a File, etc.
     984                  try: name = factory.__name__
     985                  except AttributeError: pass
     986                  else: factory = None
     987          if not factory:
     988              # They passed us None, or we picked up a name from a specified
     989              # class, so return the FS method.  (Note that we *don't*
     990              # use our own self.{Dir,File} methods because that would
     991              # cause env.subst() to be called twice on the file name,
     992              # interfering with files that have $$ in them.)
     993              factory = getattr(self.fs, name)
     994          return factory
     995
     996      memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
     997
     998 -    def _gsm(self):
     999          try:
    1000              return self._memo['_gsm']
    1001          except KeyError:
    1002              pass
    1003
    1004          result = {}
    1005
    1006          try:
    1007              scanners = self._dict['SCANNERS']
    1008          except KeyError:
    1009              pass
    1010          else:
    1011              # Reverse the scanner list so that, if multiple scanners
    1012              # claim they can scan the same suffix, earlier scanners
    1013              # in the list will overwrite later scanners, so that
    1014              # the result looks like a "first match" to the user.
    1015              if not SCons.Util.is_List(scanners):
    1016                  scanners = [scanners]
    1017              else:
    1018                  scanners = scanners[:] # copy so reverse() doesn't mod original
    1019              scanners.reverse()
    1020              for scanner in scanners:
    1021                  for k in scanner.get_skeys(self):
    1022                      result[k] = scanner
    1023
    1024          self._memo['_gsm'] = result
    1025
    1026          return result
    1027
    1028 -    def get_scanner(self, skey):
    1029          """Find the appropriate scanner given a key (usually a file suffix).
    1030          """
    1031          return self._gsm().get(skey)
    1032
    1033 -    def scanner_map_delete(self, kw=None):
    1034          """Delete the cached scanner map (if we need to).
    1035          """
    1036          try:
    1037              del self._memo['_gsm']
    1038          except KeyError:
    1039              pass
    1040
    1041 -    def _update(self, dict):
    1042          """Update an environment's values directly, bypassing the normal
    1043          checks that occur when users try to set items.
    1044          """
    1045          self._dict.update(dict)
    1046
    1047 -    def get_src_sig_type(self):
    1048          try:
    1049              return self.src_sig_type
    1050          except AttributeError:
    1051              t = SCons.Defaults.DefaultEnvironment().src_sig_type
    1052              self.src_sig_type = t
    1053              return t
    1054
    1055 -    def get_tgt_sig_type(self):
    1056          try:
    1057              return self.tgt_sig_type
    1058          except AttributeError:
    1059              t = SCons.Defaults.DefaultEnvironment().tgt_sig_type
    1060              self.tgt_sig_type = t
    1061              return t
    1062
    1063      #######################################################################
    1064      # Public methods for manipulating an Environment.  These begin with
    1065      # upper-case letters.  The essential characteristic of methods in
    1066      # this section is that they do *not* have corresponding same-named
    1067      # global functions.  For example, a stand-alone Append() function
    1068      # makes no sense, because Append() is all about appending values to
    1069      # an Environment's construction variables.
    1070      #######################################################################
    1071
    1072 -    def Append(self, **kw):
    1073          """Append values to existing construction variables
    1074          in an Environment.
    1075          """
    1076          kw = copy_non_reserved_keywords(kw)
    1077          for key, val in kw.items():
    1078              # It would be easier on the eyes to write this using
    1079              # "continue" statements whenever we finish processing an item,
    1080              # but Python 1.5.2 apparently doesn't let you use "continue"
    1081              # within try:-except: blocks, so we have to nest our code.
    1082              try:
    1083                  orig = self._dict[key]
    1084              except KeyError:
    1085                  # No existing variable in the environment, so just set
    1086                  # it to the new value.
    1087                  self._dict[key] = val
    1088              else:
    1089                  try:
    1090                      # Check if the original looks like a dictionary.
    1091                      # If it is, we can't just try adding the value because
    1092                      # dictionaries don't have __add__() methods, and
    1093                      # things like UserList will incorrectly coerce the
    1094                      # original dict to a list (which we don't want).
    1095                      update_dict = orig.update
    1096                  except AttributeError:
    1097                      try:
    1098                          # Most straightforward:  just try to add them
    1099                          # together.  This will work in most cases, when the
    1100                          # original and new values are of compatible types.
    1101                          self._dict[key] = orig + val
    1102                      except (KeyError, TypeError):
    1103                          try:
    1104                              # Check if the original is a list.
    1105                              add_to_orig = orig.append
    1106                          except AttributeError:
    1107                              # The original isn't a list, but the new
    1108                              # value is (by process of elimination),
    1109                              # so insert the original in the new value
    1110                              # (if there's one to insert) and replace
    1111                              # the variable with it.
    1112                              if orig:
    1113                                  val.insert(0, orig)
    1114                              self._dict[key] = val
    1115                          else:
    1116                              # The original is a list, so append the new
    1117                              # value to it (if there's a value to append).
    1118                              if val:
    1119                                  add_to_orig(val)
    1120                  else:
    1121                      # The original looks like a dictionary, so update it
    1122                      # based on what we think the value looks like.
    1123                      if SCons.Util.is_List(val):
    1124                          for v in val:
    1125                              orig[v] = None
    1126                      else:
    1127                          try:
    1128                              update_dict(val)
    1129                          except (AttributeError, TypeError, ValueError):
    1130                              if SCons.Util.is_Dict(val):
    1131                                  for k, v in val.items():
    1132                                      orig[k] = v
    1133                              else:
    1134                                  orig[val] = None
    1135          self.scanner_map_delete(kw)
    1136
    1137 -    def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
    1138          """Append path elements to the path 'name' in the 'ENV'
    1139          dictionary for this environment.  Will only add any particular
    1140          path once, and will normpath and normcase all paths to help
    1141          assure this.  This can also handle the case where the env
    1142          variable is a list instead of a string.
    1143          """
    1144
    1145          orig = ''
    1146          if self._dict.has_key(envname) and self._dict[envname].has_key(name):
    1147              orig = self._dict[envname][name]
    1148
    1149          nv = SCons.Util.AppendPath(orig, newpath, sep)
    1150
    1151          if not self._dict.has_key(envname):
    1152              self._dict[envname] = {}
    1153
    1154          self._dict[envname][name] = nv
    1155
    1156 -    def AppendUnique(self, **kw):
    1157          """Append values to existing construction variables
    1158          in an Environment, if they're not already there.
    1159          """
    1160          kw = copy_non_reserved_keywords(kw)
    1161          for key, val in kw.items():
    1162              if not self._dict.has_key(key) or self._dict[key] in ('', None):
    1163                  self._dict[key] = val
    1164              elif SCons.Util.is_Dict(self._dict[key]) and \
    1165                   SCons.Util.is_Dict(val):
    1166                  self._dict[key].update(val)
    1167              elif SCons.Util.is_List(val):
    1168                  dk = self._dict[key]
    1169                  if not SCons.Util.is_List(dk):
    1170                      dk = [dk]
    1171                  val = filter(lambda x, dk=dk: x not in dk, val)
    1172                  self._dict[key] = dk + val
    1173              else:
    1174                  dk = self._dict[key]
    1175                  if SCons.Util.is_List(dk):
    1176                      # By elimination, val is not a list.  Since dk is a
    1177                      # list, wrap val in a list first.
    1178                      if not val in dk:
    1179                          self._dict[key] = dk + [val]
    1180                  else:
    1181                      self._dict[key] = self._dict[key] + val
    1182          self.scanner_map_delete(kw)
    1183
    1184 -    def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
    1185          """Return a copy of a construction Environment.  The
    1186          copy is like a Python "deep copy"--that is, independent
    1187          copies are made recursively of each objects--except that
    1188          a reference is copied when an object is not deep-copyable
    1189          (like a function).  There are no references to any mutable
    1190          objects in the original Environment.
    1191          """
    1192          clone = copy.copy(self)
    1193          clone._dict = semi_deepcopy(self._dict)
    1194
    1195          try:
    1196              cbd = clone._dict['BUILDERS']
    1197          except KeyError:
    1198              pass
    1199          else:
    1200              clone._dict['BUILDERS'] = BuilderDict(cbd, clone)
    1201
    1202          # Check the methods added via AddMethod() and re-bind them to
    1203          # the cloned environment.  Only do this if the attribute hasn't
    1204          # been overwritten by the user explicitly and still points to
    1205          # the added method.
    1206          clone.added_methods = []
    1207          for mw in self.added_methods:
    1208              if mw == getattr(self, mw.name):
    1209                  clone.added_methods.append(mw.clone(clone))
    1210
    1211          clone._memo = {}
    1212
    1213          # Apply passed-in variables before the tools
    1214          # so the tools can use the new variables
    1215          kw = copy_non_reserved_keywords(kw)
    1216          new = {}
    1217          for key, value in kw.items():
    1218              new[key] = SCons.Subst.scons_subst_once(value, self, key)
    1219          apply(clone.Replace, (), new)
    1220
    1221          apply_tools(clone, tools, toolpath)
    1222
    1223          # apply them again in case the tools overwrote them
    1224          apply(clone.Replace, (), new)
    1225
    1226          # Finally, apply any flags to be merged in
    1227          if parse_flags: clone.MergeFlags(parse_flags)
    1228
    1229          if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone')
    1230          return clone
    1231
    1232 -    def Copy(self, *args, **kw):
    1233          global _warn_copy_deprecated
    1234          if _warn_copy_deprecated:
    1235              msg = "The env.Copy() method is deprecated; use the env.Clone() method instead."
    1236              SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg)
    1237              _warn_copy_deprecated = False
    1238          return apply(self.Clone, args, kw)
    1239
    1240 -    def _changed_build(self, dependency, target, prev_ni):
    1241          if dependency.changed_state(target, prev_ni):
    1242              return 1
    1243          return self.decide_source(dependency, target, prev_ni)
    1244
    1245 -    def _changed_content(self, dependency, target, prev_ni):
    1246          return dependency.changed_content(target, prev_ni)
    1247
    1248 -    def _changed_source(self, dependency, target, prev_ni):
    1249          target_env = dependency.get_build_env()
    1250          type = target_env.get_tgt_sig_type()
    1251          if type == 'source':
    1252              return target_env.decide_source(dependency, target, prev_ni)
    1253          else:
    1254              return target_env.decide_target(dependency, target, prev_ni)
    1255
    1256 -    def _changed_timestamp_then_content(self, dependency, target, prev_ni):
    1257          return dependency.changed_timestamp_then_content(target, prev_ni)
    1258
    1259 -    def _changed_timestamp_newer(self, dependency, target, prev_ni):
    1260          return dependency.changed_timestamp_newer(target, prev_ni)
    1261
    1262 -    def _changed_timestamp_match(self, dependency, target, prev_ni):
    1263          return dependency.changed_timestamp_match(target, prev_ni)
    1264
    1265 -    def _copy_from_cache(self, src, dst):
    1266          return self.fs.copy(src, dst)
    1267
    1268 -    def _copy2_from_cache(self, src, dst):
    1269          return self.fs.copy2(src, dst)
    1270
    1271 -    def Decider(self, function):
    1272          copy_function = self._copy2_from_cache
    1273          if function in ('MD5', 'content'):
    1274              if not SCons.Util.md5:
    1275                  raise UserError, "MD5 signatures are not available in this version of Python."
    1276              function = self._changed_content
    1277          elif function == 'MD5-timestamp':
    1278              function = self._changed_timestamp_then_content
    1279          elif function in ('timestamp-newer', 'make'):
    1280              function = self._changed_timestamp_newer
    1281              copy_function = self._copy_from_cache
    1282          elif function == 'timestamp-match':
    1283              function = self._changed_timestamp_match
    1284          elif not callable(function):
    1285              raise UserError, "Unknown Decider value %s" % repr(function)
    1286
    1287          # We don't use AddMethod because we don't want to turn the
    1288          # function, which only expects three arguments, into a bound
    1289          # method, which would add self as an initial, fourth argument.
    1290          self.decide_target = function
    1291          self.decide_source = function
    1292
    1293          self.copy_from_cache = copy_function
    1294
    1295 -    def Detect(self, progs):
    1296          """Return the first available program in progs.
    1297          """
    1298          if not SCons.Util.is_List(progs):
    1299              progs = [ progs ]
    1300          for prog in progs:
    1301              path = self.WhereIs(prog)
    1302              if path: return prog
    1303          return None
    1304
    1305 -    def Dictionary(self, *args):
    1306          if not args:
    1307              return self._dict
    1308          dlist = map(lambda x, s=self: s._dict[x], args)
    1309          if len(dlist) == 1:
    1310              dlist = dlist[0]
    1311          return dlist
    1312
    1313 -    def Dump(self, key = None):
    1314          """
    1315          Using the standard Python pretty printer, dump the contents of the
    1316          scons build environment to stdout.
    1317
    1318          If the key passed in is anything other than None, then that will
    1319          be used as an index into the build environment dictionary and
    1320          whatever is found there will be fed into the pretty printer. Note
    1321          that this key is case sensitive.
    1322          """
    1323          import pprint
    1324          pp = pprint.PrettyPrinter(indent=2)
    1325          if key:
    1326              dict = self.Dictionary(key)
    1327          else:
    1328              dict = self.Dictionary()
    1329          return pp.pformat(dict)
    1330
    1331 -    def FindIxes(self, paths, prefix, suffix):
    1332          """
    1333          Search a list of paths for something that matches the prefix and suffix.
    1334
    1335          paths - the list of paths or nodes.
    1336          prefix - construction variable for the prefix.
    1337          suffix - construction variable for the suffix.
    1338          """
    1339
    1340          suffix = self.subst('$'+suffix)
    1341          prefix = self.subst('$'+prefix)
    1342
    1343          for path in paths:
    1344              dir,name = os.path.split(str(path))
    1345              if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix:
    1346                  return path
    1347
    1348 -    def ParseConfig(self, command, function=None, unique=1):
    1349          """
    1350          Use the specified function to parse the output of the command
    1351          in order to modify the current environment.  The 'command' can
    1352          be a string or a list of strings representing a command and
    1353          its arguments.  'Function' is an optional argument that takes
    1354          the environment, the output of the command, and the unique flag.
    1355          If no function is specified, MergeFlags, which treats the output
    1356          as the result of a typical 'X-config' command (i.e. gtk-config),
    1357          will merge the output into the appropriate variables.
    1358          """
    1359          if function is None:
    1360              def parse_conf(env, cmd, unique=unique):
    1361                  return env.MergeFlags(cmd, unique)
    1362              function = parse_conf
    1363          if SCons.Util.is_List(command):
    1364              command = string.join(command)
    1365          command = self.subst(command)
    1366          return function(self, self.backtick(command))
    1367
    1368 -    def ParseDepends(self, filename, must_exist=None, only_one=0):
    1369          """
    1370          Parse a mkdep-style file for explicit dependencies.  This is
    1371          completely abusable, and should be unnecessary in the "normal"
    1372          case of proper SCons configuration, but it may help make
    1373          the transition from a Make hierarchy easier for some people
    1374          to swallow.  It can also be genuinely useful when using a tool
    1375          that can write a .d file, but for which writing a scanner would
    1376          be too complicated.
    1377          """
    1378          filename = self.subst(filename)
    1379          try:
    1380              fp = open(filename, 'r')
    1381          except IOError:
    1382              if must_exist:
    1383                  raise
    1384              return
    1385          lines = SCons.Util.LogicalLines(fp).readlines()
    1386          lines = filter(lambda l: l[0] != '#', lines)
    1387          tdlist = []
    1388          for line in lines:
    1389              try:
    1390                  target, depends = string.split(line, ':', 1)
    1391              except (AttributeError, TypeError, ValueError):
    1392                  # Python 1.5.2 throws TypeError if line isn't a string,
    1393                  # Python 2.x throws AttributeError because it tries
    1394                  # to call line.split().  Either can throw ValueError
    1395                  # if the line doesn't split into two or more elements.
    1396                  pass
    1397              else:
    1398                  tdlist.append((string.split(target), string.split(depends)))
    1399          if only_one:
    1400              targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist))
    1401              if len(targets) > 1:
    1402                  raise SCons.Errors.UserError, "More than one dependency target found in `%s':  %s" % (filename, targets)
    1403          for target, depends in tdlist:
    1404              self.Depends(target, depends)
    1405
    1406 -    def Platform(self, platform):
    1407          platform = self.subst(platform)
    1408          return SCons.Platform.Platform(platform)(self)
    1409
    1410 -    def Prepend(self, **kw):
    1411          """Prepend values to existing construction variables
    1412          in an Environment.
    1413          """
    1414          kw = copy_non_reserved_keywords(kw)
    1415          for key, val in kw.items():
    1416              # It would be easier on the eyes to write this using
    1417              # "continue" statements whenever we finish processing an item,
    1418              # but Python 1.5.2 apparently doesn't let you use "continue"
    1419              # within try:-except: blocks, so we have to nest our code.
    1420              try:
    1421                  orig = self._dict[key]
    1422              except KeyError:
    1423                  # No existing variable in the environment, so just set
    1424                  # it to the new value.
    1425                  self._dict[key] = val
    1426              else:
    1427                  try:
    1428                      # Check if the original looks like a dictionary.
    1429                      # If it is, we can't just try adding the value because
    1430                      # dictionaries don't have __add__() methods, and
    1431                      # things like UserList will incorrectly coerce the
    1432                      # original dict to a list (which we don't want).
    1433                      update_dict = orig.update
    1434                  except AttributeError:
    1435                      try:
    1436                          # Most straightforward:  just try to add them
    1437                          # together.  This will work in most cases, when the
    1438                          # original and new values are of compatible types.
    1439                          self._dict[key] = val + orig
    1440                      except (KeyError, TypeError):
    1441                          try:
    1442                              # Check if the added value is a list.
    1443                              add_to_val = val.append
    1444                          except AttributeError:
    1445                              # The added value isn't a list, but the
    1446                              # original is (by process of elimination),
    1447                              # so insert the the new value in the original
    1448                              # (if there's one to insert).
    1449                              if val:
    1450                                  orig.insert(0, val)
    1451                          else:
    1452                              # The added value is a list, so append
    1453                              # the original to it (if there's a value
    1454                              # to append).
    1455                              if orig:
    1456                                  add_to_val(orig)
    1457                              self._dict[key] = val
    1458                  else:
    1459                      # The original looks like a dictionary, so update it
    1460                      # based on what we think the value looks like.
    1461                      if SCons.Util.is_List(val):
    1462                          for v in val:
    1463                              orig[v] = None
    1464                      else:
    1465                          try:
    1466                              update_dict(val)
    1467                          except (AttributeError, TypeError, ValueError):
    1468                              if SCons.Util.is_Dict(val):
    1469                                  for k, v in val.items():
    1470                                      orig[k] = v
    1471                              else:
    1472                                  orig[val] = None
    1473          self.scanner_map_delete(kw)
    1474
    1475 -    def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
    1476          """Prepend path elements to the path 'name' in the 'ENV'
    1477          dictionary for this environment.  Will only add any particular
    1478          path once, and will normpath and normcase all paths to help
    1479          assure this.  This can also handle the case where the env
    1480          variable is a list instead of a string.
    1481          """
    1482
    1483          orig = ''
    1484          if self._dict.has_key(envname) and self._dict[envname].has_key(name):
    1485              orig = self._dict[envname][name]
    1486
    1487          nv = SCons.Util.PrependPath(orig, newpath, sep)
    1488
    1489          if not self._dict.has_key(envname):
    1490              self._dict[envname] = {}
    1491
    1492          self._dict[envname][name] = nv
    1493
    1494 -    def PrependUnique(self, **kw):
    1495          """Append values to existing construction variables
    1496          in an Environment, if they're not already there.
    1497          """
    1498          kw = copy_non_reserved_keywords(kw)
    1499          for key, val in kw.items():
    1500              if not self._dict.has_key(key) or self._dict[key] in ('', None):
    1501                  self._dict[key] = val
    1502              elif SCons.Util.is_Dict(self._dict[key]) and \
    1503                   SCons.Util.is_Dict(val):
    1504                  self._dict[key].update(val)
    1505              elif SCons.Util.is_List(val):
    1506                  dk = self._dict[key]
    1507                  if not SCons.Util.is_List(dk):
    1508                      dk = [dk]
    1509                  val = filter(lambda x, dk=dk: x not in dk, val)
    1510                  self._dict[key] = val + dk
    1511              else:
    1512                  dk = self._dict[key]
    1513                  if SCons.Util.is_List(dk):
    1514                      # By elimination, val is not a list.  Since dk is a
    1515                      # list, wrap val in a list first.
    1516                      if not val in dk:
    1517                          self._dict[key] = [val] + dk
    1518                  else:
    1519                      self._dict[key] = val + dk
    1520          self.scanner_map_delete(kw)
    1521
    1522 -    def Replace(self, **kw):
    1523          """Replace existing construction variables in an Environment
    1524          with new construction variables and/or values.
    1525          """
    1526          try:
    1527              kwbd = kw['BUILDERS']
    1528          except KeyError:
    1529              pass
    1530          else:
    1531              kwbd = semi_deepcopy(kwbd)
    1532              del kw['BUILDERS']
    1533              self.__setitem__('BUILDERS', kwbd)
    1534          kw = copy_non_reserved_keywords(kw)
    1535          self._update(semi_deepcopy(kw))
    1536          self.scanner_map_delete(kw)
    1537
    1538 -    def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
    1539          """
    1540          Replace old_prefix with new_prefix and old_suffix with new_suffix.
    1541
    1542          env - Environment used to interpolate variables.
    1543          path - the path that will be modified.
    1544          old_prefix - construction variable for the old prefix.
    1545          old_suffix - construction variable for the old suffix.
    1546          new_prefix - construction variable for the new prefix.
    1547          new_suffix - construction variable for the new suffix.
    1548          """
    1549          old_prefix = self.subst('$'+old_prefix)
    1550          old_suffix = self.subst('$'+old_suffix)
    1551
    1552          new_prefix = self.subst('$'+new_prefix)
    1553          new_suffix = self.subst('$'+new_suffix)
    1554
    1555          dir,name = os.path.split(str(path))
    1556          if name[:len(old_prefix)] == old_prefix:
    1557              name = name[len(old_prefix):]
    1558          if name[-len(old_suffix):] == old_suffix:
    1559              name = name[:-len(old_suffix)]
    1560          return os.path.join(dir, new_prefix+name+new_suffix)
    1561
    1562 -    def SetDefault(self, **kw):
    1563          for k in kw.keys():
    1564              if self._dict.has_key(k):
    1565                  del kw[k]
    1566          apply(self.Replace, (), kw)
    1567
    1568 -    def _find_toolpath_dir(self, tp):
    1569          return self.fs.Dir(self.subst(tp)).srcnode().abspath
    1570
    1571 -    def Tool(self, tool, toolpath=None, **kw):
    1572          if SCons.Util.is_String(tool):
    1573              tool = self.subst(tool)
    1574              if toolpath is None:
    1575                  toolpath = self.get('toolpath', [])
    1576              toolpath = map(self._find_toolpath_dir, toolpath)
    1577              tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
    1578          tool(self)
    1579
    1580 -    def WhereIs(self, prog, path=None, pathext=None, reject=[]):
    1581          """Find prog in the path.
    1582          """
    1583          if path is None:
    1584              try:
    1585                  path = self['ENV']['PATH']
    1586              except KeyError:
    1587                  pass
    1588          elif SCons.Util.is_String(path):
    1589              path = self.subst(path)
    1590          if pathext is None:
    1591              try:
    1592                  pathext = self['ENV']['PATHEXT']
    1593              except KeyError:
    1594                  pass
    1595          elif SCons.Util.is_String(pathext):
    1596              pathext = self.subst(pathext)
    1597          path = SCons.Util.WhereIs(prog, path, pathext, reject)
    1598          if path: return path
    1599          return None
    1600
    1601      #######################################################################
    1602      # Public methods for doing real "SCons stuff" (manipulating
    1603      # dependencies, setting attributes on targets, etc.).  These begin
    1604      # with upper-case letters.  The essential characteristic of methods
    1605      # in this section is that they all *should* have corresponding
    1606      # same-named global functions.
    1607      #######################################################################
    1608
    1609 -    def Action(self, *args, **kw):
    1610          def subst_string(a, self=self):
    1611              if SCons.Util.is_String(a):
    1612                  a = self.subst(a)
    1613              return a
    1614          nargs = map(subst_string, args)
    1615          nkw = self.subst_kw(kw)
    1616          return apply(SCons.Action.Action, nargs, nkw)
    1617
    1618 -    def AddPreAction(self, files, action):
    1619          nodes = self.arg2nodes(files, self.fs.Entry)
    1620          action = SCons.Action.Action(action)
    1621          uniq = {}
    1622          for executor in map(lambda n: n.get_executor(), nodes):
    1623              uniq[executor] = 1
    1624          for executor in uniq.keys():
    1625              executor.add_pre_action(action)
    1626          return nodes
    1627
    1628 -    def AddPostAction(self, files, action):
    1629          nodes = self.arg2nodes(files, self.fs.Entry)
    1630          action = SCons.Action.Action(action)
    1631          uniq = {}
    1632          for executor in map(lambda n: n.get_executor(), nodes):
    1633              uniq[executor] = 1
    1634          for executor in uniq.keys():
    1635              executor.add_post_action(action)
    1636          return nodes
    1637
    1638 -    def Alias(self, target, source=[], action=None, **kw):
    1639          tlist = self.arg2nodes(target, self.ans.Alias)
    1640          if not SCons.Util.is_List(source):
    1641              source = [source]
    1642          source = filter(None, source)
    1643
    1644          if not action:
    1645              if not source:
    1646                  # There are no source files and no action, so just
    1647                  # return a target list of classic Alias Nodes, without
    1648                  # any builder.  The externally visible effect is that
    1649                  # this will make the wrapping Script.BuildTask class
    1650                  # say that there's "Nothing to be done" for this Alias,
    1651                  # instead of that it's "up to date."
    1652                  return tlist
    1653
    1654              # No action, but there are sources.  Re-call all the target
    1655              # builders to add the sources to each target.
    1656              result = []
    1657              for t in tlist:
    1658                  bld = t.get_builder(AliasBuilder)
    1659                  result.extend(bld(self, t, source))
    1660              return result
    1661
    1662          nkw = self.subst_kw(kw)
    1663          nkw.update({
    1664              'action'            : SCons.Action.Action(action),
    1665              'source_factory'    : self.fs.Entry,
    1666              'multi'             : 1,
    1667              'is_explicit'       : None,
    1668          })
    1669          bld = apply(SCons.Builder.Builder, (), nkw)
    1670
    1671          # Apply the Builder separately to each target so that the Aliases
    1672          # stay separate.  If we did one "normal" Builder call with the
    1673          # whole target list, then all of the target Aliases would be
    1674          # associated under a single Executor.
    1675          result = []
    1676          for t in tlist:
    1677              # Calling the convert() method will cause a new Executor to be
    1678              # created from scratch, so we have to explicitly initialize
    1679              # it with the target's existing sources, plus our new ones,
    1680              # so nothing gets lost.
    1681              b = t.get_builder()
    1682              if b is None or b is AliasBuilder:
    1683                  b = bld
    1684              else:
    1685                  nkw['action'] = b.action + action
    1686                  b = apply(SCons.Builder.Builder, (), nkw)
    1687              t.convert()
    1688              result.extend(b(self, t, t.sources + source))
    1689          return result
    1690
    1691 -    def AlwaysBuild(self, *targets):
    1692          tlist = []
    1693          for t in targets:
    1694              tlist.extend(self.arg2nodes(t, self.fs.Entry))
    1695          for t in tlist:
    1696              t.set_always_build()
    1697          return tlist
    1698
    1699 -    def BuildDir(self, *args, **kw):
    1700          if kw.has_key('build_dir'):
    1701              kw['variant_dir'] = kw['build_dir']
    1702              del kw['build_dir']
    1703          return apply(self.VariantDir, args, kw)
    1704
    1705 -    def Builder(self, **kw):
    1706          nkw = self.subst_kw(kw)
    1707          return apply(SCons.Builder.Builder, [], nkw)
    1708
    1709 -    def CacheDir(self, path):
    1710          import SCons.CacheDir
    1711          if not path is None:
    1712              path = self.subst(path)
    1713          self._CacheDir_path = path
    1714
    1715 -    def Clean(self, targets, files):
    1716          global CleanTargets
    1717          tlist = self.arg2nodes(targets, self.fs.Entry)
    1718          flist = self.arg2nodes(files, self.fs.Entry)
    1719          for t in tlist:
    1720              try:
    1721                  CleanTargets[t].extend(flist)
    1722              except KeyError:
    1723                  CleanTargets[t] = flist
    1724
    1725 -    def Configure(self, *args, **kw):
    1726          nargs = [self]
    1727          if args:
    1728              nargs = nargs + self.subst_list(args)[0]
    1729          nkw = self.subst_kw(kw)
    1730          nkw['_depth'] = kw.get('_depth', 0) + 1
    1731          try:
    1732              nkw['custom_tests'] = self.subst_kw(nkw['custom_tests'])
    1733          except KeyError:
    1734              pass
    1735          return apply(SCons.SConf.SConf, nargs, nkw)
    1736
    1737 -    def Command(self, target, source, action, **kw):
    1738          """Builds the supplied target files from the supplied
    1739          source files using the supplied action.  Action may
    1740          be any type that the Builder constructor will accept
    1741          for an action."""
    1742          bkw = {
    1743              'action' : action,
    1744              'target_factory' : self.fs.Entry,
    1745              'source_factory' : self.fs.Entry,
    1746          }
    1747          try: bkw['source_scanner'] = kw['source_scanner']
    1748          except KeyError: pass
    1749          else: del kw['source_scanner']
    1750          bld = apply(SCons.Builder.Builder, (), bkw)
    1751          return apply(bld, (self, target, source), kw)
    1752
    1753 -    def Depends(self, target, dependency):
    1754          """Explicity specify that 'target's depend on 'dependency'."""
    1755          tlist = self.arg2nodes(target, self.fs.Entry)
    1756          dlist = self.arg2nodes(dependency, self.fs.Entry)
    1757          for t in tlist:
    1758              t.add_dependency(dlist)
    1759          return tlist
    1760
    1761 -    def Dir(self, name, *args, **kw):
    1762          """
    1763          """
    1764          s = self.subst(name)
    1765          if SCons.Util.is_Sequence(s):
    1766              result=[]
    1767              for e in s:
    1768                  result.append(apply(self.fs.Dir, (e,) + args, kw))
    1769              return result
    1770          return apply(self.fs.Dir, (s,) + args, kw)
    1771
    1772 -    def NoClean(self, *targets):
    1773          """Tags a target so that it will not be cleaned by -c"""
    1774          tlist = []
    1775          for t in targets:
    1776              tlist.extend(self.arg2nodes(t, self.fs.Entry))
    1777          for t in tlist:
    1778              t.set_noclean()
    1779          return tlist
    1780
    1781 -    def NoCache(self, *targets):
    1782          """Tags a target so that it will not be cached"""
    1783          tlist = []
    1784          for t in targets:
    1785              tlist.extend(self.arg2nodes(t, self.fs.Entry))
    1786          for t in tlist:
    1787              t.set_nocache()
    1788          return tlist
    1789
    1790 -    def Entry(self, name, *args, **kw):
    1791          """
    1792          """
    1793          s = self.subst(name)
    1794          if SCons.Util.is_Sequence(s):
    1795              result=[]
    1796              for e in s:
    1797                  result.append(apply(self.fs.Entry, (e,) + args, kw))
    1798              return result
    1799          return apply(self.fs.Entry, (s,) + args, kw)
    1800
    1801 -    def Environment(self, **kw):
    1802          return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
    1803
    1804 -    def Execute(self, action, *args, **kw):
    1805          """Directly execute an action through an Environment
    1806          """
    1807          action = apply(self.Action, (action,) + args, kw)
    1808          result = action([], [], self)
    1809          if isinstance(result, SCons.Errors.BuildError):
    1810              return result.status
    1811          else:
    1812              return result
    1813
    1814 -    def File(self, name, *args, **kw):
    1815          """
    1816          """
    1817          s = self.subst(name)
    1818          if SCons.Util.is_Sequence(s):
    1819              result=[]
    1820              for e in s:
    1821                  result.append(apply(self.fs.File, (e,) + args, kw))
    1822              return result
    1823          return apply(self.fs.File, (s,) + args, kw)
    1824
    1825 -    def FindFile(self, file, dirs):
    1826          file = self.subst(file)
    1827          nodes = self.arg2nodes(dirs, self.fs.Dir)
    1828          return SCons.Node.FS.find_file(file, tuple(nodes))
    1829
    1830 -    def Flatten(self, sequence):
    1831          return SCons.Util.flatten(sequence)
    1832
    1833 -    def GetBuildPath(self, files):
    1834          result = map(str, self.arg2nodes(files, self.fs.Entry))
    1835          if SCons.Util.is_List(files):
    1836              return result
    1837          else:
    1838              return result[0]
    1839
    1840 -    def Glob(self, pattern, ondisk=True, source=False, strings=False):
    1841          return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
    1842
    1843 -    def Ignore(self, target, dependency):
    1844          """Ignore a dependency."""
    1845          tlist = self.arg2nodes(target, self.fs.Entry)
    1846          dlist = self.arg2nodes(dependency, self.fs.Entry)
    1847          for t in tlist:
    1848              t.add_ignore(dlist)
    1849          return tlist
    1850
    1851 -    def Literal(self, string):
    1852          return SCons.Subst.Literal(string)
    1853
    1854 -    def Local(self, *targets):
    1855          ret = []
    1856          for targ in targets:
    1857              if isinstance(targ, SCons.Node.Node):
    1858                  targ.set_local()
    1859                  ret.append(targ)
    1860              else:
    1861                  for t in self.arg2nodes(targ, self.fs.Entry):
    1862                     t.set_local()
    1863                     ret.append(t)
    1864          return ret
    1865
    1866 -    def Precious(self, *targets):
    1867          tlist = []
    1868          for t in targets:
    1869              tlist.extend(self.arg2nodes(t, self.fs.Entry))
    1870          for t in tlist:
    1871              t.set_precious()
    1872          return tlist
    1873
    1874 -    def Repository(self, *dirs, **kw):
    1875          dirs = self.arg2nodes(list(dirs), self.fs.Dir)
    1876          apply(self.fs.Repository, dirs, kw)
    1877
    1878 -    def Requires(self, target, prerequisite):
    1879          """Specify that 'prerequisite' must be built before 'target',
    1880          (but 'target' does not actually depend on 'prerequisite'
    1881          and need not be rebuilt if it changes)."""
    1882          tlist = self.arg2nodes(target, self.fs.Entry)
    1883          plist = self.arg2nodes(prerequisite, self.fs.Entry)
    1884          for t in tlist:
    1885              t.add_prerequisite(plist)
    1886          return tlist
    1887
    1888 -    def Scanner(self, *args, **kw):
    1889          nargs = []
    1890          for arg in args:
    1891              if SCons.Util.is_String(arg):
    1892                  arg = self.subst(arg)
    1893              nargs.append(arg)
    1894          nkw = self.subst_kw(kw)
    1895          return apply(SCons.Scanner.Base, nargs, nkw)
    1896
    1897 -    def SConsignFile(self, name=".sconsign", dbm_module=None):
    1898          if not name is None:
    1899              name = self.subst(name)
    1900              if not os.path.isabs(name):
    1901                  name = os.path.join(str(self.fs.SConstruct_dir), name)
    1902          SCons.SConsign.File(name, dbm_module)
    1903
    1904 -    def SideEffect(self, side_effect, target):
    1905          """Tell scons that side_effects are built as side
    1906          effects of building targets."""
    1907          side_effects = self.arg2nodes(side_effect, self.fs.Entry)
    1908          targets = self.arg2nodes(target, self.fs.Entry)
    1909
    1910          for side_effect in side_effects:
    1911              if side_effect.multiple_side_effect_has_builder():
    1912                  raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
    1913              side_effect.add_source(targets)
    1914              side_effect.side_effect = 1
    1915              self.Precious(side_effect)
    1916              for target in targets:
    1917                  target.side_effects.append(side_effect)
    1918          return side_effects
    1919
    1920 -    def SourceCode(self, entry, builder):
    1921          """Arrange for a source code builder for (part of) a tree."""
    1922          entries = self.arg2nodes(entry, self.fs.Entry)
    1923          for entry in entries:
    1924              entry.set_src_builder(builder)
    1925          return entries
    1926
    1927 -    def SourceSignatures(self, type):
    1928          global _warn_source_signatures_deprecated
    1929          if _warn_source_signatures_deprecated:
    1930              msg = "The env.SourceSignatures() method is deprecated;\n" + \
    1931                    "\tconvert your build to use the env.Decider() method instead."
    1932              SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg)
    1933              _warn_source_signatures_deprecated = False
    1934          type = self.subst(type)
    1935          self.src_sig_type = type
    1936          if type == 'MD5':
    1937              if not SCons.Util.md5:
    1938                  raise UserError, "MD5 signatures are not available in this version of Python."
    1939              self.decide_source = self._changed_content
    1940          elif type == 'timestamp':
    1941              self.decide_source = self._changed_timestamp_match
    1942          else:
    1943              raise UserError, "Unknown source signature type '%s'" % type
    1944
    1945 -    def Split(self, arg):
    1946          """This function converts a string or list into a list of strings
    1947          or Nodes.  This makes things easier for users by allowing files to
    1948          be specified as a white-space separated list to be split.
    1949          The input rules are:
    1950              - A single string containing names separated by spaces. These will be
    1951                split apart at the spaces.
    1952              - A single Node instance
    1953              - A list containing either strings or Node instances. Any strings
    1954                in the list are not split at spaces.
    1955          In all cases, the function returns a list of Nodes and strings."""
    1956          if SCons.Util.is_List(arg):
    1957              return map(self.subst, arg)
    1958          elif SCons.Util.is_String(arg):
    1959              return string.split(self.subst(arg))
    1960          else:
    1961              return [self.subst(arg)]
    1962
    1963 -    def TargetSignatures(self, type):
    1964          global _warn_target_signatures_deprecated
    1965          if _warn_target_signatures_deprecated:
    1966              msg = "The env.TargetSignatures() method is deprecated;\n" + \
    1967                    "\tconvert your build to use the env.Decider() method instead."
    1968              SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg)
    1969              _warn_target_signatures_deprecated = False
    1970          type = self.subst(type)
    1971          self.tgt_sig_type = type
    1972          if type in ('MD5', 'content'):
    1973              if not SCons.Util.md5:
    1974                  raise UserError, "MD5 signatures are not available in this version of Python."
    1975              self.decide_target = self._changed_content
    1976          elif type == 'timestamp':
    1977              self.decide_target = self._changed_timestamp_match
    1978          elif type == 'build':
    1979              self.decide_target = self._changed_build
    1980          elif type == 'source':
    1981              self.decide_target = self._changed_source
    1982          else:
    1983              raise UserError, "Unknown target signature type '%s'"%type
    1984
    1985 -    def Value(self, value, built_value=None):
    1986          """
    1987          """
    1988          return SCons.Node.Python.Value(value, built_value)
    1989
    1990 -    def VariantDir(self, variant_dir, src_dir, duplicate=1):
    1991          variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0]
    1992          src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
    1993          self.fs.VariantDir(variant_dir, src_dir, duplicate)
    1994
    1995 -    def FindSourceFiles(self, node='.'):
    1996          """ returns a list of all source files.
    1997          """
    1998          node = self.arg2nodes(node, self.fs.Entry)[0]
    1999
    2000          sources = []
    2001          # Uncomment this and get rid of the global definition when we
    2002          # drop support for pre-2.2 Python versions.
    2003          #def build_source(ss, result):
    2004          #    for s in ss:
    2005          #        if isinstance(s, SCons.Node.FS.Dir):
    2006          #            build_source(s.all_children(), result)
    2007          #        elif s.has_builder():
    2008          #            build_source(s.sources, result)
    2009          #        elif isinstance(s.disambiguate(), SCons.Node.FS.File):
    2010          #            result.append(s)
    2011          build_source(node.all_children(), sources)
    2012
    2013          # now strip the build_node from the sources by calling the srcnode
    2014          # function
    2015          def get_final_srcnode(file):
    2016              srcnode = file.srcnode()
    2017              while srcnode != file.srcnode():
    2018                  srcnode = file.srcnode()
    2019              return srcnode
    2020
    2021          # get the final srcnode for all nodes, this means stripping any
    2022          # attached build node.
    2023          map( get_final_srcnode, sources )
    2024
    2025          # remove duplicates
    2026          return list(set(sources))
    2027
    2028 -    def FindInstalledFiles(self):
    2029          """ returns the list of all targets of the Install and InstallAs Builder.
    2030          """
    2031          from SCons.Tool import install
    2032          if install._UNIQUE_INSTALLED_FILES is None:
    2033              install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES)
    2034          return install._UNIQUE_INSTALLED_FILES
    2035
    2036 -class OverrideEnvironment(Base):
    2037      """A proxy that overrides variables in a wrapped construction
    2038      environment by returning values from an overrides dictionary in
    2039      preference to values from the underlying subject environment.
    2040
    2041      This is a lightweight (I hope) proxy that passes through most use of
    2042      attributes to the underlying Environment.Base class, but has just
    2043      enough additional methods defined to act like a real construction
    2044      environment with overridden values.  It can wrap either a Base
    2045      construction environment, or another OverrideEnvironment, which
    2046      can in turn nest arbitrary OverrideEnvironments...
    2047
    2048      Note that we do *not* call the underlying base class
    2049      (SubsitutionEnvironment) initialization, because we get most of those
    2050      from proxying the attributes of the subject construction environment.
    2051      But because we subclass SubstitutionEnvironment, this class also
    2052      has inherited arg2nodes() and subst*() methods; those methods can't
    2053      be proxied because they need *this* object's methods to fetch the
    2054      values from the overrides dictionary.
    2055      """
    2056
    2057      if SCons.Memoize.use_memoizer:
    2058          __metaclass__ = SCons.Memoize.Memoized_Metaclass
    2059
    2060 -    def __init__(self, subject, overrides={}):
    2061          if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment')
    2062          self.__dict__['__subject'] = subject
    2063          self.__dict__['overrides'] = overrides
    2064
    2065      # Methods that make this class act like a proxy.
    2066 -    def __getattr__(self, name):
    2067          return getattr(self.__dict__['__subject'], name)
    2068 -    def __setattr__(self, name, value):
    2069          setattr(self.__dict__['__subject'], name, value)
    2070
    2071      # Methods that make this class act like a dictionary.
    2072 -    def __getitem__(self, key):
    2073          try:
    2074              return self.__dict__['overrides'][key]
    2075          except KeyError:
    2076              return self.__dict__['__subject'].__getitem__(key)
    2077 -    def __setitem__(self, key, value):
    2078          if not is_valid_construction_var(key):
    2079              raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key
    2080          self.__dict__['overrides'][key] = value
    2081 -    def __delitem__(self, key):
    2082          try:
    2083              del self.__dict__['overrides'][key]
    2084          except KeyError:
    2085              deleted = 0
    2086          else:
    2087              deleted = 1
    2088          try:
    2089              result = self.__dict__['__subject'].__delitem__(key)
    2090          except KeyError:
    2091              if not deleted:
    2092                  raise
    2093              result = None
    2094          return result
    2095 -    def get(self, key, default=None):
    2096          """Emulates the get() method of dictionaries."""
    2097          try:
    2098              return self.__dict__['overrides'][key]
    2099          except KeyError:
    2100              return self.__dict__['__subject'].get(key, default)
    2101 -    def has_key(self, key):
    2102          try:
    2103              self.__dict__['overrides'][key]
    2104              return 1
    2105          except KeyError:
    2106              return self.__dict__['__subject'].has_key(key)
    2107 -    def Dictionary(self):
    2108          """Emulates the items() method of dictionaries."""
    2109          d = self.__dict__['__subject'].Dictionary().copy()
    2110          d.update(self.__dict__['overrides'])
    2111          return d
    2112 -    def items(self):
    2113          """Emulates the items() method of dictionaries."""
    2114          return self.Dictionary().items()
    2115
    2116      # Overridden private construction environment methods.
    2117 -    def _update(self, dict):
    2118          """Update an environment's values directly, bypassing the normal
    2119          checks that occur when users try to set items.
    2120          """
    2121          self.__dict__['overrides'].update(dict)
    2122
    2123 -    def gvars(self):
    2124          return self.__dict__['__subject'].gvars()
    2125
    2126 -    def lvars(self):
    2127          lvars = self.__dict__['__subject'].lvars()
    2128          lvars.update(self.__dict__['overrides'])
    2129          return lvars
    2130
    2131      # Overridden public construction environment methods.
    2132 -    def Replace(self, **kw):
    2133          kw = copy_non_reserved_keywords(kw)
    2134          self.__dict__['overrides'].update(semi_deepcopy(kw))
    2135
    2136  # The entry point that will be used by the external world
    2137  # to refer to a construction environment.  This allows the wrapper
    2138  # interface to extend a construction environment for its own purposes
    2139  # by subclassing SCons.Environment.Base and then assigning the
    2140  # class to SCons.Environment.Environment.
    2141
    2142  Environment = Base
    2143
    2144  # An entry point for returning a proxy subclass instance that overrides
    2145  # the subst*() methods so they don't actually perform construction
    2146  # variable substitution.  This is specifically intended to be the shim
    2147  # layer in between global function calls (which don't want construction
    2148  # variable substitution) and the DefaultEnvironment() (which would
    2149  # substitute variables if left to its own devices)."""
    2150  #
    2151  # We have to wrap this in a function that allows us to delay definition of
    2152  # the class until it's necessary, so that when it subclasses Environment
    2153  # it will pick up whatever Environment subclass the wrapper interface
    2154  # might have assigned to SCons.Environment.Environment.
    2155
    2156 -def NoSubstitutionProxy(subject):
    2157      class _NoSubstitutionProxy(Environment):
    2158          def __init__(self, subject):
    2159              self.__dict__['__subject'] = subject
    2160          def __getattr__(self, name):
    2161              return getattr(self.__dict__['__subject'], name)
    2162          def __setattr__(self, name, value):
    2163              return setattr(self.__dict__['__subject'], name, value)
    2164          def raw_to_mode(self, dict):
    2165              try:
    2166                  raw = dict['raw']
    2167              except KeyError:
    2168                  pass
    2169              else:
    2170                  del dict['raw']
    2171                  dict['mode'] = raw
    2172          def subst(self, string, *args, **kwargs):
    2173              return string
    2174          def subst_kw(self, kw, *args, **kwargs):
    2175              return kw
    2176          def subst_list(self, string, *args, **kwargs):
    2177              nargs = (string, self,) + args
    2178              nkw = kwargs.copy()
    2179              nkw['gvars'] = {}
    2180              self.raw_to_mode(nkw)
    2181              return apply(SCons.Subst.scons_subst_list, nargs, nkw)
    2182          def subst_target_source(self, string, *args, **kwargs):
    2183              nargs = (string, self,) + args
    2184              nkw = kwargs.copy()
    2185              nkw['gvars'] = {}
    2186              self.raw_to_mode(nkw)
    2187              return apply(SCons.Subst.scons_subst, nargs, nkw)
    2188      return _NoSubstitutionProxy(subject)
    2189