SCons.Script.Main

Package SCons :: Package Script :: Module Main |
[hide private]
[frames] | no frames]

# Source Code for Module SCons.Script.Main

       1  """SCons.Script
       2
       3  This file implements the main() function used by the scons script.
       4
       5  Architecturally, this *is* the scons script, and will likely only be
       6  called from the external "scons" wrapper.  Consequently, anything here
       7  should not be, or be considered, part of the build engine.  If it's
       8  something that we expect other software to want to use, it should go in
       9  some other module.  If it's specific to the "scons" script invocation,
      10  it goes here.
      11
      12  """
      13
      14  #
      15  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
      16  #
      17  # Permission is hereby granted, free of charge, to any person obtaining
      18  # a copy of this software and associated documentation files (the
      19  # "Software"), to deal in the Software without restriction, including
      20  # without limitation the rights to use, copy, modify, merge, publish,
      21  # distribute, sublicense, and/or sell copies of the Software, and to
      22  # permit persons to whom the Software is furnished to do so, subject to
      23  # the following conditions:
      24  #
      25  # The above copyright notice and this permission notice shall be included
      26  # in all copies or substantial portions of the Software.
      27  #
      28  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
      29  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
      30  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      31  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
      32  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
      33  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
      34  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      35  #
      36
      37  __revision__ = "src/engine/SCons/Script/Main.py 2725 2008/03/31 12:52:02 knight"
      38
      39  import SCons.compat
      40
      41  import os
      42  import os.path
      43  import string
      44  import sys
      45  import time
      46  import traceback
      47
      48  # Strip the script directory from sys.path() so on case-insensitive
      49  # (Windows) systems Python doesn't think that the "scons" script is the
      50  # "SCons" package.  Replace it with our own version directory so, if
      51  # if they're there, we pick up the right version of the build engine
      52  # modules.
      53  #sys.path = [os.path.join(sys.prefix,
      54  #                         'lib',
      55  #                         'scons-%d' % SCons.__version__)] + sys.path[1:]
      56
      57  import SCons.CacheDir
      58  import SCons.Debug
      59  import SCons.Defaults
      60  import SCons.Environment
      61  import SCons.Errors
      62  import SCons.Job
      63  import SCons.Node
      64  import SCons.Node.FS
      65  import SCons.SConf
      66  import SCons.Script
      67  import SCons.Taskmaster
      68  import SCons.Util
      69  import SCons.Warnings
      70
      71  import SCons.Script.Interactive
      72
      73 -def fetch_win32_parallel_msg():
      74      # A subsidiary function that exists solely to isolate this import
      75      # so we don't have to pull it in on all platforms, and so that an
      76      # in-line "import" statement in the _main() function below doesn't
      77      # cause warnings about local names shadowing use of the 'SCons'
      78      # globl in nest scopes and UnboundLocalErrors and the like in some
      79      # versions (2.1) of Python.
      80      import SCons.Platform.win32
      81      SCons.Platform.win32.parallel_msg
      82
      83  #
      84
      85 -class SConsPrintHelpException(Exception):
      86      pass
      87
      88  display = SCons.Util.display
      89  progress_display = SCons.Util.DisplayEngine()
      90
      91  first_command_start = None
      92  last_command_end = None
      93
      94 -class Progressor:
      95      prev = ''
      96      count = 0
      97      target_string = '$TARGET'
      98
      99 -    def __init__(self, obj, interval=1, file=None, overwrite=False):
     100          if file is None:
     101              file = sys.stdout
     102
     103          self.obj = obj
     104          self.file = file
     105          self.interval = interval
     106          self.overwrite = overwrite
     107
     108          if callable(obj):
     109              self.func = obj
     110          elif SCons.Util.is_List(obj):
     111              self.func = self.spinner
     112          elif string.find(obj, self.target_string) != -1:
     113              self.func = self.replace_string
     114          else:
     115              self.func = self.string
     116
     117 -    def write(self, s):
     118          self.file.write(s)
     119          self.file.flush()
     120          self.prev = s
     121
     122 -    def erase_previous(self):
     123          if self.prev:
     124              length = len(self.prev)
     125              if self.prev[-1] in ('\n', '\r'):
     126                  length = length - 1
     127              self.write(' ' * length + '\r')
     128              self.prev = ''
     129
     130 -    def spinner(self, node):
     131          self.write(self.obj[self.count % len(self.obj)])
     132
     133 -    def string(self, node):
     134          self.write(self.obj)
     135
     136 -    def replace_string(self, node):
     137          self.write(string.replace(self.obj, self.target_string, str(node)))
     138
     139 -    def __call__(self, node):
     140          self.count = self.count + 1
     141          if (self.count % self.interval) == 0:
     142              if self.overwrite:
     143                  self.erase_previous()
     144              self.func(node)
     145
     146  ProgressObject = SCons.Util.Null()
     147
     148 -def Progress(*args, **kw):
     149      global ProgressObject
     150      ProgressObject = apply(Progressor, args, kw)
     151
     152  # Task control.
     153  #
     154
     155  _BuildFailures = []
     156
     157 -def GetBuildFailures():
     158      return _BuildFailures
     159
     160 -class BuildTask(SCons.Taskmaster.Task):
     161      """An SCons build task."""
     162      progress = ProgressObject
     163
     164 -    def display(self, message):
     165          display('scons: ' + message)
     166
     167 -    def prepare(self):
     168          self.progress(self.targets[0])
     169          return SCons.Taskmaster.Task.prepare(self)
     170
     171 -    def execute(self):
     172          for target in self.targets:
     173              if target.get_state() == SCons.Node.up_to_date:
     174                  continue
     175              if target.has_builder() and not hasattr(target.builder, 'status'):
     176                  if print_time:
     177                      start_time = time.time()
     178                      global first_command_start
     179                      if first_command_start is None:
     180                          first_command_start = start_time
     181                  SCons.Taskmaster.Task.execute(self)
     182                  if print_time:
     183                      global cumulative_command_time
     184                      global last_command_end
     185                      finish_time = time.time()
     186                      last_command_end = finish_time
     187                      cumulative_command_time = cumulative_command_time+finish_time-start_time
     188                      sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time))
     189                  break
     190          else:
     191              if self.top and target.has_builder():
     192                  display("scons: `%s' is up to date." % str(self.node))
     193
     194 -    def do_failed(self, status=2):
     195          _BuildFailures.append(self.exception[1])
     196          global exit_status
     197          if self.options.ignore_errors:
     198              SCons.Taskmaster.Task.executed(self)
     199          elif self.options.keep_going:
     200              SCons.Taskmaster.Task.fail_continue(self)
     201              exit_status = status
     202          else:
     203              SCons.Taskmaster.Task.fail_stop(self)
     204              exit_status = status
     205
     206 -    def executed(self):
     207          t = self.targets[0]
     208          if self.top and not t.has_builder() and not t.side_effect:
     209              if not t.exists():
     210                  sys.stderr.write("scons: *** Do not know how to make target `%s'." % t)
     211                  if not self.options.keep_going:
     212                      sys.stderr.write("  Stop.")
     213                  sys.stderr.write("\n")
     214                  self.do_failed()
     215              else:
     216                  print "scons: Nothing to be done for `%s'." % t
     217                  SCons.Taskmaster.Task.executed(self)
     218          else:
     219              SCons.Taskmaster.Task.executed(self)
     220
     221 -    def failed(self):
     222          # Handle the failure of a build task.  The primary purpose here
     223          # is to display the various types of Errors and Exceptions
     224          # appropriately.
     225          status = 2
     226          exc_info = self.exc_info()
     227          try:
     228              t, e, tb = exc_info
     229          except ValueError:
     230              t, e = exc_info
     231              tb = None
     232          if t is None:
     233              # The Taskmaster didn't record an exception for this Task;
     234              # see if the sys module has one.
     235              t, e = sys.exc_info()[:2]
     236
     237          def nodestring(n):
     238              if not SCons.Util.is_List(n):
     239                  n = [ n ]
     240              return string.join(map(str, n), ', ')
     241
     242          errfmt = "scons: *** [%s] %s\n"
     243
     244          if t == SCons.Errors.BuildError:
     245              tname = nodestring(e.node)
     246              errstr = e.errstr
     247              if e.filename:
     248                  errstr = e.filename + ': ' + errstr
     249              sys.stderr.write(errfmt % (tname, errstr))
     250          elif t == SCons.Errors.TaskmasterException:
     251              tname = nodestring(e.node)
     252              sys.stderr.write(errfmt % (tname, e.errstr))
     253              type, value, trace = e.exc_info
     254              traceback.print_exception(type, value, trace)
     255          elif t == SCons.Errors.ExplicitExit:
     256              status = e.status
     257              tname = nodestring(e.node)
     258              errstr = 'Explicit exit, status %s' % status
     259              sys.stderr.write(errfmt % (tname, errstr))
     260          else:
     261              if e is None:
     262                  e = t
     263              s = str(e)
     264              if t == SCons.Errors.StopError and not self.options.keep_going:
     265                  s = s + '  Stop.'
     266              sys.stderr.write("scons: *** %s\n" % s)
     267
     268              if tb and print_stacktrace:
     269                  sys.stderr.write("scons: internal stack trace:\n")
     270                  traceback.print_tb(tb, file=sys.stderr)
     271
     272          self.do_failed(status)
     273
     274          self.exc_clear()
     275
     276 -    def postprocess(self):
     277          if self.top:
     278              t = self.targets[0]
     279              for tp in self.options.tree_printers:
     280                  tp.display(t)
     281              if self.options.debug_includes:
     282                  tree = t.render_include_tree()
     283                  if tree:
     284                      print
     285                      print tree
     286          SCons.Taskmaster.Task.postprocess(self)
     287
     288 -    def make_ready(self):
     289          """Make a task ready for execution"""
     290          SCons.Taskmaster.Task.make_ready(self)
     291          if self.out_of_date and self.options.debug_explain:
     292              explanation = self.out_of_date[0].explain()
     293              if explanation:
     294                  sys.stdout.write("scons: " + explanation)
     295
     296 -class CleanTask(SCons.Taskmaster.Task):
     297      """An SCons clean task."""
     298 -    def fs_delete(self, path, pathstr, remove=1):
     299          try:
     300              if os.path.exists(path):
     301                  if os.path.isfile(path):
     302                      if remove: os.unlink(path)
     303                      display("Removed " + pathstr)
     304                  elif os.path.isdir(path) and not os.path.islink(path):
     305                      # delete everything in the dir
     306                      entries = os.listdir(path)
     307                      # Sort for deterministic output (os.listdir() Can
     308                      # return entries in a random order).
     309                      entries.sort()
     310                      for e in entries:
     311                          p = os.path.join(path, e)
     312                          s = os.path.join(pathstr, e)
     313                          if os.path.isfile(p):
     314                              if remove: os.unlink(p)
     315                              display("Removed " + s)
     316                          else:
     317                              self.fs_delete(p, s, remove)
     318                      # then delete dir itself
     319                      if remove: os.rmdir(path)
     320                      display("Removed directory " + pathstr)
     321          except (IOError, OSError), e:
     322              print "scons: Could not remove '%s':" % pathstr, e.strerror
     323
     324 -    def show(self):
     325          target = self.targets[0]
     326          if (target.has_builder() or target.side_effect) and not target.noclean:
     327              for t in self.targets:
     328                  if not t.isdir():
     329                      display("Removed " + str(t))
     330          if SCons.Environment.CleanTargets.has_key(target):
     331              files = SCons.Environment.CleanTargets[target]
     332              for f in files:
     333                  self.fs_delete(f.abspath, str(f), 0)
     334
     335 -    def remove(self):
     336          target = self.targets[0]
     337          if (target.has_builder() or target.side_effect) and not target.noclean:
     338              for t in self.targets:
     339                  try:
     340                      removed = t.remove()
     341                  except OSError, e:
     342                      # An OSError may indicate something like a permissions
     343                      # issue, an IOError would indicate something like
     344                      # the file not existing.  In either case, print a
     345                      # message and keep going to try to remove as many
     346                      # targets aa possible.
     347                      print "scons: Could not remove '%s':" % str(t), e.strerror
     348                  else:
     349                      if removed:
     350                          display("Removed " + str(t))
     351          if SCons.Environment.CleanTargets.has_key(target):
     352              files = SCons.Environment.CleanTargets[target]
     353              for f in files:
     354                  self.fs_delete(f.abspath, str(f))
     355
     356      execute = remove
     357
     358      # We want the Taskmaster to update the Node states (and therefore
     359      # handle reference counts, etc.), but we don't want to call
     360      # back to the Node's post-build methods, which would do things
     361      # we don't want, like store .sconsign information.
     362      executed = SCons.Taskmaster.Task.executed_without_callbacks
     363
     364      # Have the taskmaster arrange to "execute" all of the targets, because
     365      # we'll figure out ourselves (in remove() or show() above) whether
     366      # anything really needs to be done.
     367      make_ready = SCons.Taskmaster.Task.make_ready_all
     368
     369 -    def prepare(self):
     370          pass
     371
     372 -class QuestionTask(SCons.Taskmaster.Task):
     373      """An SCons task for the -q (question) option."""
     374 -    def prepare(self):
     375          pass
     376
     377 -    def execute(self):
     378          if self.targets[0].get_state() != SCons.Node.up_to_date or \
     379             (self.top and not self.targets[0].exists()):
     380              global exit_status
     381              exit_status = 1
     382              self.tm.stop()
     383
     384 -    def executed(self):
     385          pass
     386
     387
     388 -class TreePrinter:
     389 -    def __init__(self, derived=False, prune=False, status=False):
     390          self.derived = derived
     391          self.prune = prune
     392          self.status = status
     393 -    def get_all_children(self, node):
     394          return node.all_children()
     395 -    def get_derived_children(self, node):
     396          children = node.all_children(None)
     397          return filter(lambda x: x.has_builder(), children)
     398 -    def display(self, t):
     399          if self.derived:
     400              func = self.get_derived_children
     401          else:
     402              func = self.get_all_children
     403          s = self.status and 2 or 0
     404          SCons.Util.print_tree(t, func, prune=self.prune, showtags=s)
     405
     406
     407 -def python_version_string():
     408      return string.split(sys.version)[0]
     409
     410 -def python_version_unsupported(version=sys.version_info):
     411      return version < (1, 5, 2)
     412
     413 -def python_version_deprecated(version=sys.version_info):
     414      return version < (2, 2, 0)
     415
     416
     417  # Global variables
     418
     419  print_objects = 0
     420  print_memoizer = 0
     421  print_stacktrace = 0
     422  print_time = 0
     423  sconscript_time = 0
     424  cumulative_command_time = 0
     425  exit_status = 0 # exit status, assume success by default
     426  num_jobs = None
     427  delayed_warnings = []
     428
     429 -class FakeOptionParser:
     430      """
     431      A do-nothing option parser, used for the initial OptionsParser variable.
     432
     433      During normal SCons operation, the OptionsParser is created right
     434      away by the main() function.  Certain tests scripts however, can
     435      introspect on different Tool modules, the initialization of which
     436      can try to add a new, local option to an otherwise uninitialized
     437      OptionsParser object.  This allows that introspection to happen
     438      without blowing up.
     439
     440      """
     441 -    class FakeOptionValues:
     442 -        def __getattr__(self, attr):
     443              return None
     444      values = FakeOptionValues()
     445 -    def add_local_option(self, *args, **kw):
     446          pass
     447
     448  OptionsParser = FakeOptionParser()
     449
     450 -def AddOption(*args, **kw):
     451      if not kw.has_key('default'):
     452          kw['default'] = None
     453      result = apply(OptionsParser.add_local_option, args, kw)
     454      return result
     455
     456 -def GetOption(name):
     457      return getattr(OptionsParser.values, name)
     458
     459 -def SetOption(name, value):
     460      return OptionsParser.values.set_option(name, value)
     461
     462  #
     463 -class Stats:
     464 -    def __init__(self):
     465          self.stats = []
     466          self.labels = []
     467          self.append = self.do_nothing
     468          self.print_stats = self.do_nothing
     469 -    def enable(self, outfp):
     470          self.outfp = outfp
     471          self.append = self.do_append
     472          self.print_stats = self.do_print
     473 -    def do_nothing(self, *args, **kw):
     474          pass
     475
     476 -class CountStats(Stats):
     477 -    def do_append(self, label):
     478          self.labels.append(label)
     479          self.stats.append(SCons.Debug.fetchLoggedInstances())
     480 -    def do_print(self):
     481          stats_table = {}
     482          for s in self.stats:
     483              for n in map(lambda t: t[0], s):
     484                  stats_table[n] = [0, 0, 0, 0]
     485          i = 0
     486          for s in self.stats:
     487              for n, c in s:
     488                  stats_table[n][i] = c
     489              i = i + 1
     490          keys = stats_table.keys()
     491          keys.sort()
     492          self.outfp.write("Object counts:\n")
     493          pre = ["   "]
     494          post = ["   %s\n"]
     495          l = len(self.stats)
     496          fmt1 = string.join(pre + [' %7s']*l + post, '')
     497          fmt2 = string.join(pre + [' %7d']*l + post, '')
     498          labels = self.labels[:l]
     499          labels.append(("", "Class"))
     500          self.outfp.write(fmt1 % tuple(map(lambda x: x[0], labels)))
     501          self.outfp.write(fmt1 % tuple(map(lambda x: x[1], labels)))
     502          for k in keys:
     503              r = stats_table[k][:l] + [k]
     504              self.outfp.write(fmt2 % tuple(r))
     505
     506  count_stats = CountStats()
     507
     508 -class MemStats(Stats):
     509 -    def do_append(self, label):
     510          self.labels.append(label)
     511          self.stats.append(SCons.Debug.memory())
     512 -    def do_print(self):
     513          fmt = 'Memory %-32s %12d\n'
     514          for label, stats in map(None, self.labels, self.stats):
     515              self.outfp.write(fmt % (label, stats))
     516
     517  memory_stats = MemStats()
     518
     519  # utility functions
     520
     521 -def _scons_syntax_error(e):
     522      """Handle syntax errors. Print out a message and show where the error
     523      occurred.
     524      """
     525      etype, value, tb = sys.exc_info()
     526      lines = traceback.format_exception_only(etype, value)
     527      for line in lines:
     528          sys.stderr.write(line+'\n')
     529      sys.exit(2)
     530
     531 -def find_deepest_user_frame(tb):
     532      """
     533      Find the deepest stack frame that is not part of SCons.
     534
     535      Input is a "pre-processed" stack trace in the form
     536      returned by traceback.extract_tb() or traceback.extract_stack()
     537      """
     538
     539      tb.reverse()
     540
     541      # find the deepest traceback frame that is not part
     542      # of SCons:
     543      for frame in tb:
     544          filename = frame[0]
     545          if string.find(filename, os.sep+'SCons'+os.sep) == -1:
     546              return frame
     547      return tb[0]
     548
     549 -def _scons_user_error(e):
     550      """Handle user errors. Print out a message and a description of the
     551      error, along with the line number and routine where it occured.
     552      The file and line number will be the deepest stack frame that is
     553      not part of SCons itself.
     554      """
     555      global print_stacktrace
     556      etype, value, tb = sys.exc_info()
     557      if print_stacktrace:
     558          traceback.print_exception(etype, value, tb)
     559      filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
     560      sys.stderr.write("\nscons: *** %s\n" % value)
     561      sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
     562      sys.exit(2)
     563
     564 -def _scons_user_warning(e):
     565      """Handle user warnings. Print out a message and a description of
     566      the warning, along with the line number and routine where it occured.
     567      The file and line number will be the deepest stack frame that is
     568      not part of SCons itself.
     569      """
     570      etype, value, tb = sys.exc_info()
     571      filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
     572      sys.stderr.write("\nscons: warning: %s\n" % e)
     573      sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
     574
     575 -def _scons_internal_warning(e):
     576      """Slightly different from _scons_user_warning in that we use the
     577      *current call stack* rather than sys.exc_info() to get our stack trace.
     578      This is used by the warnings framework to print warnings."""
     579      filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
     580      sys.stderr.write("\nscons: warning: %s\n" % e[0])
     581      sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
     582
     583 -def _scons_internal_error():
     584      """Handle all errors but user errors. Print out a message telling
     585      the user what to do in this case and print a normal trace.
     586      """
     587      print 'internal error'
     588      traceback.print_exc()
     589      sys.exit(2)
     590
     591 -def _SConstruct_exists(dirname='', repositories=[]):
     592      """This function checks that an SConstruct file exists in a directory.
     593      If so, it returns the path of the file. By default, it checks the
     594      current directory.
     595      """
     596      for file in ['SConstruct', 'Sconstruct', 'sconstruct']:
     597          sfile = os.path.join(dirname, file)
     598          if os.path.isfile(sfile):
     599              return sfile
     600          if not os.path.isabs(sfile):
     601              for rep in repositories:
     602                  if os.path.isfile(os.path.join(rep, sfile)):
     603                      return sfile
     604      return None
     605
     606 -def _set_debug_values(options):
     607      global print_memoizer, print_objects, print_stacktrace, print_time
     608
     609      debug_values = options.debug
     610
     611      if "count" in debug_values:
     612          # All of the object counts are within "if __debug__:" blocks,
     613          # which get stripped when running optimized (with python -O or
     614          # from compiled *.pyo files).  Provide a warning if __debug__ is
     615          # stripped, so it doesn't just look like --debug=count is broken.
     616          enable_count = False
     617          if __debug__: enable_count = True
     618          if enable_count:
     619              count_stats.enable(sys.stdout)
     620          else:
     621              msg = "--debug=count is not supported when running SCons\n" + \
     622                    "\twith the python -O option or optimized (.pyo) modules."
     623              SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg)
     624      if "dtree" in debug_values:
     625          options.tree_printers.append(TreePrinter(derived=True))
     626      options.debug_explain = ("explain" in debug_values)
     627      if "findlibs" in debug_values:
     628          SCons.Scanner.Prog.print_find_libs = "findlibs"
     629      options.debug_includes = ("includes" in debug_values)
     630      print_memoizer = ("memoizer" in debug_values)
     631      if "memory" in debug_values:
     632          memory_stats.enable(sys.stdout)
     633      print_objects = ("objects" in debug_values)
     634      if "presub" in debug_values:
     635          SCons.Action.print_actions_presub = 1
     636      if "stacktrace" in debug_values:
     637          print_stacktrace = 1
     638      if "stree" in debug_values:
     639          options.tree_printers.append(TreePrinter(status=True))
     640      if "time" in debug_values:
     641          print_time = 1
     642      if "tree" in debug_values:
     643          options.tree_printers.append(TreePrinter())
     644
     645 -def _create_path(plist):
     646      path = '.'
     647      for d in plist:
     648          if os.path.isabs(d):
     649              path = d
     650          else:
     651              path = path + '/' + d
     652      return path
     653
     654 -def _load_site_scons_dir(topdir, site_dir_name=None):
     655      """Load the site_scons dir under topdir.
     656      Adds site_scons to sys.path, imports site_scons/site_init.py,
     657      and adds site_scons/site_tools to default toolpath."""
     658      if site_dir_name:
     659          err_if_not_found = True       # user specified: err if missing
     660      else:
     661          site_dir_name = "site_scons"
     662          err_if_not_found = False
     663
     664      site_dir = os.path.join(topdir.path, site_dir_name)
     665      if not os.path.exists(site_dir):
     666          if err_if_not_found:
     667              raise SCons.Errors.UserError, "site dir %s not found."%site_dir
     668          return
     669
     670      site_init_filename = "site_init.py"
     671      site_init_modname = "site_init"
     672      site_tools_dirname = "site_tools"
     673      sys.path = [os.path.abspath(site_dir)] + sys.path
     674      site_init_file = os.path.join(site_dir, site_init_filename)
     675      site_tools_dir = os.path.join(site_dir, site_tools_dirname)
     676      if os.path.exists(site_init_file):
     677          import imp
     678          try:
     679              fp, pathname, description = imp.find_module(site_init_modname,
     680                                                          [site_dir])
     681              try:
     682                  imp.load_module(site_init_modname, fp, pathname, description)
     683              finally:
     684                  if fp:
     685                      fp.close()
     686          except ImportError, e:
     687              sys.stderr.write("Can't import site init file '%s': %s\n"%(site_init_file, e))
     688              raise
     689          except Exception, e:
     690              sys.stderr.write("Site init file '%s' raised exception: %s\n"%(site_init_file, e))
     691              raise
     692      if os.path.exists(site_tools_dir):
     693          SCons.Tool.DefaultToolpath.append(os.path.abspath(site_tools_dir))
     694
     695 -def version_string(label, module):
     696      version = module.__version__
     697      build = module.__build__
     698      if build:
     699          if build[0] != '.':
     700              build = '.' + build
     701          version = version + build
     702      fmt = "\t%s: v%s, %s, by %s on %s\n"
     703      return fmt % (label,
     704                    version,
     705                    module.__date__,
     706                    module.__developer__,
     707                    module.__buildsys__)
     708
     709 -def _main(parser):
     710      global exit_status
     711
     712      options = parser.values
     713
     714      # Here's where everything really happens.
     715
     716      # First order of business:  set up default warnings and then
     717      # handle the user's warning options, so that we can issue (or
     718      # suppress) appropriate warnings about anything that might happen,
     719      # as configured by the user.
     720
     721      default_warnings = [ SCons.Warnings.CorruptSConsignWarning,
     722                           SCons.Warnings.DeprecatedWarning,
     723                           SCons.Warnings.DuplicateEnvironmentWarning,
     724                           SCons.Warnings.MissingSConscriptWarning,
     725                           SCons.Warnings.NoMD5ModuleWarning,
     726                           SCons.Warnings.NoMetaclassSupportWarning,
     727                           SCons.Warnings.NoObjectCountWarning,
     728                           SCons.Warnings.NoParallelSupportWarning,
     729                           SCons.Warnings.MisleadingKeywordsWarning,
     730                           SCons.Warnings.StackSizeWarning, ]
     731      for warning in default_warnings:
     732          SCons.Warnings.enableWarningClass(warning)
     733      SCons.Warnings._warningOut = _scons_internal_warning
     734      SCons.Warnings.process_warn_strings(options.warn)
     735
     736      # Now that we have the warnings configuration set up, we can actually
     737      # issue (or suppress) any warnings about warning-worthy things that
     738      # occurred while the command-line options were getting parsed.
     739      try:
     740          dw = options.delayed_warnings
     741      except AttributeError:
     742          pass
     743      else:
     744          delayed_warnings.extend(dw)
     745      for warning_type, message in delayed_warnings:
     746          SCons.Warnings.warn(warning_type, message)
     747
     748      if options.diskcheck:
     749          SCons.Node.FS.set_diskcheck(options.diskcheck)
     750
     751      # Next, we want to create the FS object that represents the outside
     752      # world's file system, as that's central to a lot of initialization.
     753      # To do this, however, we need to be in the directory from which we
     754      # want to start everything, which means first handling any relevant
     755      # options that might cause us to chdir somewhere (-C, -D, -U, -u).
     756      if options.directory:
     757          cdir = _create_path(options.directory)
     758          try:
     759              os.chdir(cdir)
     760          except OSError:
     761              sys.stderr.write("Could not change directory to %s\n" % cdir)
     762
     763      target_top = None
     764      if options.climb_up:
     765          target_top = '.'  # directory to prepend to targets
     766          script_dir = os.getcwd()  # location of script
     767          while script_dir and not _SConstruct_exists(script_dir, options.repository):
     768              script_dir, last_part = os.path.split(script_dir)
     769              if last_part:
     770                  target_top = os.path.join(last_part, target_top)
     771              else:
     772                  script_dir = ''
     773          if script_dir:
     774              display("scons: Entering directory `%s'" % script_dir)
     775              os.chdir(script_dir)
     776
     777      # Now that we're in the top-level SConstruct directory, go ahead
     778      # and initialize the FS object that represents the file system,
     779      # and make it the build engine default.
     780      fs = SCons.Node.FS.get_default_fs()
     781
     782      for rep in options.repository:
     783          fs.Repository(rep)
     784
     785      # Now that we have the FS object, the next order of business is to
     786      # check for an SConstruct file (or other specified config file).
     787      # If there isn't one, we can bail before doing any more work.
     788      scripts = []
     789      if options.file:
     790          scripts.extend(options.file)
     791      if not scripts:
     792          sfile = _SConstruct_exists(repositories=options.repository)
     793          if sfile:
     794              scripts.append(sfile)
     795
     796      if not scripts:
     797          if options.help:
     798              # There's no SConstruct, but they specified -h.
     799              # Give them the options usage now, before we fail
     800              # trying to read a non-existent SConstruct file.
     801              raise SConsPrintHelpException
     802          raise SCons.Errors.UserError, "No SConstruct file found."
     803
     804      if scripts[0] == "-":
     805          d = fs.getcwd()
     806      else:
     807          d = fs.File(scripts[0]).dir
     808      fs.set_SConstruct_dir(d)
     809
     810      _set_debug_values(options)
     811      SCons.Node.implicit_cache = options.implicit_cache
     812      SCons.Node.implicit_deps_changed = options.implicit_deps_changed
     813      SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
     814
     815      if options.no_exec:
     816          SCons.SConf.dryrun = 1
     817          SCons.Action.execute_actions = None
     818      if options.question:
     819          SCons.SConf.dryrun = 1
     820      if options.clean:
     821          SCons.SConf.SetBuildType('clean')
     822      if options.help:
     823          SCons.SConf.SetBuildType('help')
     824      SCons.SConf.SetCacheMode(options.config)
     825      SCons.SConf.SetProgressDisplay(progress_display)
     826
     827      if options.no_progress or options.silent:
     828          progress_display.set_mode(0)
     829
     830      if options.site_dir:
     831          _load_site_scons_dir(d, options.site_dir)
     832      elif not options.no_site_dir:
     833          _load_site_scons_dir(d)
     834
     835      if options.include_dir:
     836          sys.path = options.include_dir + sys.path
     837
     838      # That should cover (most of) the options.  Next, set up the variables
     839      # that hold command-line arguments, so the SConscript files that we
     840      # read and execute have access to them.
     841      targets = []
     842      xmit_args = []
     843      for a in parser.largs:
     844          if a[0] == '-':
     845              continue
     846          if '=' in a:
     847              xmit_args.append(a)
     848          else:
     849              targets.append(a)
     850      SCons.Script._Add_Targets(targets + parser.rargs)
     851      SCons.Script._Add_Arguments(xmit_args)
     852
     853      # If stdout is not a tty, replace it with a wrapper object to call flush
     854      # after every write.
     855      #
     856      # Tty devices automatically flush after every newline, so the replacement
     857      # isn't necessary.  Furthermore, if we replace sys.stdout, the readline
     858      # module will no longer work.  This affects the behavior during
     859      # --interactive mode.  --interactive should only be used when stdin and
     860      # stdout refer to a tty.
     861      if not sys.stdout.isatty():
     862          sys.stdout = SCons.Util.Unbuffered(sys.stdout)
     863      if not sys.stderr.isatty():
     864          sys.stderr = SCons.Util.Unbuffered(sys.stderr)
     865
     866      memory_stats.append('before reading SConscript files:')
     867      count_stats.append(('pre-', 'read'))
     868
     869      # And here's where we (finally) read the SConscript files.
     870
     871      progress_display("scons: Reading SConscript files ...")
     872
     873      start_time = time.time()
     874      try:
     875          for script in scripts:
     876              SCons.Script._SConscript._SConscript(fs, script)
     877      except SCons.Errors.StopError, e:
     878          # We had problems reading an SConscript file, such as it
     879          # couldn't be copied in to the VariantDir.  Since we're just
     880          # reading SConscript files and haven't started building
     881          # things yet, stop regardless of whether they used -i or -k
     882          # or anything else.
     883          sys.stderr.write("scons: *** %s  Stop.\n" % e)
     884          exit_status = 2
     885          sys.exit(exit_status)
     886      global sconscript_time
     887      sconscript_time = time.time() - start_time
     888
     889      progress_display("scons: done reading SConscript files.")
     890
     891      memory_stats.append('after reading SConscript files:')
     892      count_stats.append(('post-', 'read'))
     893
     894      # Re-{enable,disable} warnings in case they disabled some in
     895      # the SConscript file.
     896      #
     897      # We delay enabling the PythonVersionWarning class until here so that,
     898      # if they explicity disabled it in either in the command line or in
     899      # $SCONSFLAGS, or in the SConscript file, then the search through
     900      # the list of deprecated warning classes will find that disabling
     901      # first and not issue the warning.
     902      SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning)
     903      SCons.Warnings.process_warn_strings(options.warn)
     904
     905      # Now that we've read the SConscript files, we can check for the
     906      # warning about deprecated Python versions--delayed until here
     907      # in case they disabled the warning in the SConscript files.
     908      if python_version_deprecated():
     909          msg = "Support for pre-2.2 Python (%s) is deprecated.\n" + \
     910                "    If this will cause hardship, contact dev@scons.tigris.org."
     911          SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning,
     912                              msg % python_version_string())
     913
     914      if not options.help:
     915          SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
     916
     917      # Now re-parse the command-line options (any to the left of a '--'
     918      # argument, that is) with any user-defined command-line options that
     919      # the SConscript files may have added to the parser object.  This will
     920      # emit the appropriate error message and exit if any unknown option
     921      # was specified on the command line.
     922
     923      parser.preserve_unknown_options = False
     924      parser.parse_args(parser.largs, options)
     925
     926      if options.help:
     927          help_text = SCons.Script.help_text
     928          if help_text is None:
     929              # They specified -h, but there was no Help() inside the
     930              # SConscript files.  Give them the options usage.
     931              raise SConsPrintHelpException
     932          else:
     933              print help_text
     934              print "Use scons -H for help about command-line options."
     935          exit_status = 0
     936          return
     937
     938      # Change directory to the top-level SConstruct directory, then tell
     939      # the Node.FS subsystem that we're all done reading the SConscript
     940      # files and calling Repository() and VariantDir() and changing
     941      # directories and the like, so it can go ahead and start memoizing
     942      # the string values of file system nodes.
     943
     944      fs.chdir(fs.Top)
     945
     946      SCons.Node.FS.save_strings(1)
     947
     948      # Now that we've read the SConscripts we can set the options
     949      # that are SConscript settable:
     950      SCons.Node.implicit_cache = options.implicit_cache
     951      SCons.Node.FS.set_duplicate(options.duplicate)
     952      fs.set_max_drift(options.max_drift)
     953      if not options.stack_size is None:
     954          SCons.Job.stack_size = options.stack_size
     955
     956      platform = SCons.Platform.platform_module()
     957
     958      if options.interactive:
     959          SCons.Script.Interactive.interact(fs, OptionsParser, options,
     960                                            targets, target_top)
     961
     962      else:
     963
     964          # Build the targets
     965          nodes = _build_targets(fs, options, targets, target_top)
     966          if not nodes:
     967              exit_status = 2
     968
     969 -def _build_targets(fs, options, targets, target_top):
     970
     971      progress_display.set_mode(not (options.no_progress or options.silent))
     972      display.set_mode(not options.silent)
     973      SCons.Action.print_actions          = not options.silent
     974      SCons.Action.execute_actions        = not options.no_exec
     975      SCons.SConf.dryrun                  = options.no_exec
     976
     977      if options.diskcheck:
     978          SCons.Node.FS.set_diskcheck(options.diskcheck)
     979
     980      _set_debug_values(options)
     981      SCons.Node.implicit_cache = options.implicit_cache
     982      SCons.Node.implicit_deps_changed = options.implicit_deps_changed
     983      SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
     984
     985      SCons.CacheDir.cache_enabled = not options.cache_disable
     986      SCons.CacheDir.cache_debug = options.cache_debug
     987      SCons.CacheDir.cache_force = options.cache_force
     988      SCons.CacheDir.cache_show = options.cache_show
     989
     990      if options.no_exec:
     991          CleanTask.execute = CleanTask.show
     992      else:
     993          CleanTask.execute = CleanTask.remove
     994
     995      lookup_top = None
     996      if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default:
     997          # They specified targets on the command line or modified
     998          # BUILD_TARGETS in the SConscript file(s), so if they used -u,
     999          # -U or -D, we have to look up targets relative to the top,
    1000          # but we build whatever they specified.
    1001          if target_top:
    1002              lookup_top = fs.Dir(target_top)
    1003              target_top = None
    1004
    1005          targets = SCons.Script.BUILD_TARGETS
    1006      else:
    1007          # There are no targets specified on the command line,
    1008          # so if they used -u, -U or -D, we may have to restrict
    1009          # what actually gets built.
    1010          d = None
    1011          if target_top:
    1012              if options.climb_up == 1:
    1013                  # -u, local directory and below
    1014                  target_top = fs.Dir(target_top)
    1015                  lookup_top = target_top
    1016              elif options.climb_up == 2:
    1017                  # -D, all Default() targets
    1018                  target_top = None
    1019                  lookup_top = None
    1020              elif options.climb_up == 3:
    1021                  # -U, local SConscript Default() targets
    1022                  target_top = fs.Dir(target_top)
    1023                  def check_dir(x, target_top=target_top):
    1024                      if hasattr(x, 'cwd') and not x.cwd is None:
    1025                          cwd = x.cwd.srcnode()
    1026                          return cwd == target_top
    1027                      else:
    1028                          # x doesn't have a cwd, so it's either not a target,
    1029                          # or not a file, so go ahead and keep it as a default
    1030                          # target and let the engine sort it out:
    1031                          return 1
    1032                  d = filter(check_dir, SCons.Script.DEFAULT_TARGETS)
    1033                  SCons.Script.DEFAULT_TARGETS[:] = d
    1034                  target_top = None
    1035                  lookup_top = None
    1036
    1037          targets = SCons.Script._Get_Default_Targets(d, fs)
    1038
    1039      if not targets:
    1040          sys.stderr.write("scons: *** No targets specified and no Default() targets found.  Stop.\n")
    1041          return None
    1042
    1043      def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
    1044          if isinstance(x, SCons.Node.Node):
    1045              node = x
    1046          else:
    1047              node = None
    1048              # Why would ltop be None? Unfortunately this happens.
    1049              if ltop == None: ltop = ''
    1050              # Curdir becomes important when SCons is called with -u, -C,
    1051              # or similar option that changes directory, and so the paths
    1052              # of targets given on the command line need to be adjusted.
    1053              curdir = os.path.join(os.getcwd(), str(ltop))
    1054              for lookup in SCons.Node.arg2nodes_lookups:
    1055                  node = lookup(x, curdir=curdir)
    1056                  if node != None:
    1057                      break
    1058              if node is None:
    1059                  node = fs.Entry(x, directory=ltop, create=1)
    1060          if ttop and not node.is_under(ttop):
    1061              if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
    1062                  node = ttop
    1063              else:
    1064                  node = None
    1065          return node
    1066
    1067      nodes = filter(None, map(Entry, targets))
    1068
    1069      task_class = BuildTask      # default action is to build targets
    1070      opening_message = "Building targets ..."
    1071      closing_message = "done building targets."
    1072      if options.keep_going:
    1073          failure_message = "done building targets (errors occurred during build)."
    1074      else:
    1075          failure_message = "building terminated because of errors."
    1076      if options.question:
    1077          task_class = QuestionTask
    1078      try:
    1079          if options.clean:
    1080              task_class = CleanTask
    1081              opening_message = "Cleaning targets ..."
    1082              closing_message = "done cleaning targets."
    1083              if options.keep_going:
    1084                  failure_message = "done cleaning targets (errors occurred during clean)."
    1085              else:
    1086                  failure_message = "cleaning terminated because of errors."
    1087      except AttributeError:
    1088          pass
    1089
    1090      task_class.progress = ProgressObject
    1091
    1092      if options.random:
    1093          def order(dependencies):
    1094              """Randomize the dependencies."""
    1095              import random
    1096              # This is cribbed from the implementation of
    1097              # random.shuffle() in Python 2.X.
    1098              d = dependencies
    1099              for i in xrange(len(d)-1, 0, -1):
    1100                  j = int(random.random() * (i+1))
    1101                  d[i], d[j] = d[j], d[i]
    1102              return d
    1103      else:
    1104          def order(dependencies):
    1105              """Leave the order of dependencies alone."""
    1106              return dependencies
    1107
    1108      if options.taskmastertrace_file == '-':
    1109          tmtrace = sys.stdout
    1110      elif options.taskmastertrace_file:
    1111          tmtrace = open(options.taskmastertrace_file, 'wb')
    1112      else:
    1113          tmtrace = None
    1114      taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace)
    1115
    1116      # Let the BuildTask objects get at the options to respond to the
    1117      # various print_* settings, tree_printer list, etc.
    1118      BuildTask.options = options
    1119
    1120      global num_jobs
    1121      num_jobs = options.num_jobs
    1122      jobs = SCons.Job.Jobs(num_jobs, taskmaster)
    1123      if num_jobs > 1:
    1124          msg = None
    1125          if jobs.num_jobs == 1:
    1126              msg = "parallel builds are unsupported by this version of Python;\n" + \
    1127                    "\tignoring -j or num_jobs option.\n"
    1128          elif sys.platform == 'win32':
    1129              msg = fetch_win32_parallel_msg()
    1130          if msg:
    1131              SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
    1132
    1133      memory_stats.append('before building targets:')
    1134      count_stats.append(('pre-', 'build'))
    1135
    1136      try:
    1137          progress_display("scons: " + opening_message)
    1138          try:
    1139              jobs.run()
    1140          except KeyboardInterrupt:
    1141              # If we are in interactive mode, a KeyboardInterrupt
    1142              # interrupts only this current run.  Return 'nodes' normally
    1143              # so that the outer loop can clean up the nodes and continue.
    1144              if options.interactive:
    1145                  print "Build interrupted."
    1146                  # Continue and return normally
    1147      finally:
    1148          jobs.cleanup()
    1149          if exit_status:
    1150              progress_display("scons: " + failure_message)
    1151          else:
    1152              progress_display("scons: " + closing_message)
    1153          if not options.no_exec:
    1154              SCons.SConsign.write()
    1155
    1156      memory_stats.append('after building targets:')
    1157      count_stats.append(('post-', 'build'))
    1158
    1159      return nodes
    1160
    1161 -def _exec_main(parser, values):
    1162      sconsflags = os.environ.get('SCONSFLAGS', '')
    1163      all_args = string.split(sconsflags) + sys.argv[1:]
    1164
    1165      options, args = parser.parse_args(all_args, values)
    1166
    1167      if type(options.debug) == type([]) and "pdb" in options.debug:
    1168          import pdb
    1169          pdb.Pdb().runcall(_main, parser)
    1170      elif options.profile_file:
    1171          from profile import Profile
    1172
    1173          # Some versions of Python 2.4 shipped a profiler that had the
    1174          # wrong 'c_exception' entry in its dispatch table.  Make sure
    1175          # we have the right one.  (This may put an unnecessary entry
    1176          # in the table in earlier versions of Python, but its presence
    1177          # shouldn't hurt anything).
    1178          try:
    1179              dispatch = Profile.dispatch
    1180          except AttributeError:
    1181              pass
    1182          else:
    1183              dispatch['c_exception'] = Profile.trace_dispatch_return
    1184
    1185          prof = Profile()
    1186          try:
    1187              prof.runcall(_main, parser)
    1188          except SConsPrintHelpException, e:
    1189              prof.dump_stats(options.profile_file)
    1190              raise e
    1191          except SystemExit:
    1192              pass
    1193          prof.dump_stats(options.profile_file)
    1194      else:
    1195          _main(parser)
    1196
    1197 -def main():
    1198      global OptionsParser
    1199      global exit_status
    1200      global first_command_start
    1201
    1202      # Check up front for a Python version we do not support.  We
    1203      # delay the check for deprecated Python versions until later,
    1204      # after the SConscript files have been read, in case they
    1205      # disable that warning.
    1206      if python_version_unsupported():
    1207          msg = "scons: *** SCons version %s does not run under Python version %s.\n"
    1208          sys.stderr.write(msg % (SCons.__version__, python_version_string()))
    1209          sys.exit(1)
    1210
    1211      parts = ["SCons by Steven Knight et al.:\n"]
    1212      try:
    1213          parts.append(version_string("script", __main__))
    1214      except KeyboardInterrupt:
    1215          raise
    1216      except:
    1217          # On Windows there is no scons.py, so there is no
    1218          # __main__.__version__, hence there is no script version.
    1219          pass
    1220      parts.append(version_string("engine", SCons))
    1221      parts.append("Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation")
    1222      version = string.join(parts, '')
    1223
    1224      import SConsOptions
    1225      parser = SConsOptions.Parser(version)
    1226      values = SConsOptions.SConsValues(parser.get_default_values())
    1227
    1228      OptionsParser = parser
    1229
    1230      try:
    1231          _exec_main(parser, values)
    1232      except SystemExit, s:
    1233          if s:
    1234              exit_status = s
    1235      except KeyboardInterrupt:
    1236          print "Build interrupted."
    1237          sys.exit(2)
    1238      except SyntaxError, e:
    1239          _scons_syntax_error(e)
    1240      except SCons.Errors.InternalError:
    1241          _scons_internal_error()
    1242      except SCons.Errors.UserError, e:
    1243          _scons_user_error(e)
    1244      except SConsPrintHelpException:
    1245          parser.print_help()
    1246          exit_status = 0
    1247      except:
    1248          # An exception here is likely a builtin Python exception Python
    1249          # code in an SConscript file.  Show them precisely what the
    1250          # problem was and where it happened.
    1251          SCons.Script._SConscript.SConscript_exception()
    1252          sys.exit(2)
    1253
    1254      memory_stats.print_stats()
    1255      count_stats.print_stats()
    1256
    1257      if print_objects:
    1258          SCons.Debug.listLoggedInstances('*')
    1259          #SCons.Debug.dumpLoggedInstances('*')
    1260
    1261      if print_memoizer:
    1262          SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:")
    1263
    1264      # Dump any development debug info that may have been enabled.
    1265      # These are purely for internal debugging during development, so
    1266      # there's no need to control them with --debug= options; they're
    1267      # controlled by changing the source code.
    1268      SCons.Debug.dump_caller_counts()
    1269      SCons.Taskmaster.dump_stats()
    1270
    1271      if print_time:
    1272          total_time = time.time() - SCons.Script.start_time
    1273          if num_jobs == 1:
    1274              ct = cumulative_command_time
    1275          else:
    1276              if last_command_end is None or first_command_start is None:
    1277                  ct = 0.0
    1278              else:
    1279                  ct = last_command_end - first_command_start
    1280          scons_time = total_time - sconscript_time - ct
    1281          print "Total build time: %f seconds"%total_time
    1282          print "Total SConscript file execution time: %f seconds"%sconscript_time
    1283          print "Total SCons execution time: %f seconds"%scons_time
    1284          print "Total command execution time: %f seconds"%ct
    1285
    1286      sys.exit(exit_status)
    1287