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